From 911c4e13f379f887b89fd176e5878d4726484744 Mon Sep 17 00:00:00 2001 From: trustable-code Date: Thu, 6 Jul 2017 21:55:17 +0200 Subject: Change directory structure, change include concept, add nimble file --- src/common/msgbox.nim | 82 - src/common/nigui.nim | 2250 ------------------------ src/gtk3/gtk3.nim | 430 ----- src/gtk3/nigui_platform_impl.nim | 1176 ------------- src/gtk3/nigui_platform_types1.nim | 25 - src/gtk3/nigui_platform_types2.nim | 20 - src/msgbox.nim | 82 + src/nigui.nim | 2254 +++++++++++++++++++++++++ src/nigui/private/gtk3/gtk3.nim | 430 +++++ src/nigui/private/gtk3/platform_impl.nim | 1176 +++++++++++++ src/nigui/private/gtk3/platform_types1.nim | 25 + src/nigui/private/gtk3/platform_types2.nim | 20 + src/nigui/private/windows/platform_impl.nim | 1278 ++++++++++++++ src/nigui/private/windows/platform_types1.nim | 23 + src/nigui/private/windows/platform_types2.nim | 18 + src/nigui/private/windows/windows.nim | 452 +++++ src/windows/nigui_platform_impl.nim | 1278 -------------- src/windows/nigui_platform_types1.nim | 23 - src/windows/nigui_platform_types2.nim | 18 - src/windows/windows.nim | 452 ----- 20 files changed, 5758 insertions(+), 5754 deletions(-) delete mode 100755 src/common/msgbox.nim delete mode 100755 src/common/nigui.nim delete mode 100755 src/gtk3/gtk3.nim delete mode 100755 src/gtk3/nigui_platform_impl.nim delete mode 100755 src/gtk3/nigui_platform_types1.nim delete mode 100755 src/gtk3/nigui_platform_types2.nim create mode 100755 src/msgbox.nim create mode 100755 src/nigui.nim create mode 100755 src/nigui/private/gtk3/gtk3.nim create mode 100755 src/nigui/private/gtk3/platform_impl.nim create mode 100755 src/nigui/private/gtk3/platform_types1.nim create mode 100755 src/nigui/private/gtk3/platform_types2.nim create mode 100755 src/nigui/private/windows/platform_impl.nim create mode 100755 src/nigui/private/windows/platform_types1.nim create mode 100755 src/nigui/private/windows/platform_types2.nim create mode 100755 src/nigui/private/windows/windows.nim delete mode 100755 src/windows/nigui_platform_impl.nim delete mode 100755 src/windows/nigui_platform_types1.nim delete mode 100755 src/windows/nigui_platform_types2.nim delete mode 100755 src/windows/windows.nim (limited to 'src') diff --git a/src/common/msgbox.nim b/src/common/msgbox.nim deleted file mode 100755 index 5071dfb..0000000 --- a/src/common/msgbox.nim +++ /dev/null @@ -1,82 +0,0 @@ -# NiGui - extended message box - -# This module provides an extended message box. -# The message box is shown as modal window. -# The message box can have up to 3 buttons with customizable titles. -# Call the proc msgBox() to open the message box. -# It will wait until the message box is closed. -# Meaning of the result value: -# 0 - message box was closed over the window close button -# 1..3 - button 1..3 clicked - -# For an example see "example_04_msgboxes.nim". - -import nigui - -type MessageBoxWindow = ref object of WindowImpl - clickedButton: Button - -proc buttonClick(event: ClickEvent) = - cast[MessageBoxWindow](event.control.parentWindow).clickedButton = cast[Button](event.control) - event.control.parentWindow.dispose() - -proc msgBox*(parent: Window, message: string, title = "Message", button1 = "OK", button2, button3: string = nil): int {.discardable.} = - const buttonMinWidth = 100 - var window = new MessageBoxWindow - window.init() - window.title = title - - var container = newLayoutContainer(Layout_Vertical) - container.padding = 10 - window.control = container - - var labelContainer = newLayoutContainer(Layout_Horizontal) - container.add(labelContainer) - labelContainer.widthMode = WidthMode_Expand - labelContainer.heightMode = HeightMode_Expand - - var label = newLabel(message) - labelContainer.add(label) - - var buttonContainer = newLayoutContainer(Layout_Horizontal) - buttonContainer.widthMode = WidthMode_Expand - buttonContainer.xAlign = XAlign_Center - buttonContainer.spacing = 12 - container.add(buttonContainer) - var b1, b2, b3: Button - - b1 = newButton(button1) - b1.minWidth = buttonMinWidth - b1.onClick = buttonClick - buttonContainer.add(b1) - - if button2 != nil: - b2 = newButton(button2) - b2.minWidth = buttonMinWidth - b2.onClick = buttonClick - buttonContainer.add(b2) - - if button3 != nil: - b3 = newButton(button3) - b3.minWidth = buttonMinWidth - b3.onClick = buttonClick - buttonContainer.add(b3) - - window.width = min(max(label.width + 40, buttonMinWidth * 3 + 65), 600) - window.height = min(label.height, 300) + buttonContainer.height + 70 - - # Center message box on window: - window.x = parent.x + ((parent.width - window.width) div 2) - window.y = parent.y + ((parent.height - window.height) div 2) - - window.showModal(parent) - - while not window.disposed: - app.sleep(100) - - if window.clickedButton == b1: - result = 1 - elif window.clickedButton == b2: - result = 2 - elif window.clickedButton == b3: - result = 3 diff --git a/src/common/nigui.nim b/src/common/nigui.nim deleted file mode 100755 index c81c02d..0000000 --- a/src/common/nigui.nim +++ /dev/null @@ -1,2250 +0,0 @@ -# NiGui - main file - -# This file contains all common code except extra widgets. -# All public procedures are declared here. -# Public types are declared here or in the platform-specific file "nigui_platform_types". -# The platform-specific files "nigui_platform_types" and "nigui_platform_procs" will be -# included to this file. - -# ======================================================================================== -# -# Public Declaration -# -# ======================================================================================== - -# ---------------------------------------------------------------------------------------- -# Simple Types -# ---------------------------------------------------------------------------------------- - -type - Layout* = enum - Layout_Horizontal - Layout_Vertical - - XAlign* = enum - XAlign_Left - XAlign_Right - XAlign_Center - XAlign_Spread - - YAlign* = enum - YAlign_Top - YAlign_Bottom - YAlign_Center - YAlign_Spread - - WidthMode* = enum - WidthMode_Static - WidthMode_Auto - WidthMode_Fill - WidthMode_Expand - - HeightMode* = enum - HeightMode_Static - HeightMode_Auto - HeightMode_Fill - HeightMode_Expand - - MouseButton* = enum - MouseButton_Left - MouseButton_Middle - MouseButton_Right - - Color* = object - red*: byte - green*: byte - blue*: byte - alpha*: byte - - Spacing* = object - left*: int - right*: int - top*: int - bottom*: int - - Timer* = distinct int - - Key* = enum - Key_None - Key_Number0 - Key_Number1 - Key_Number2 - Key_Number3 - Key_Number4 - Key_Number5 - Key_Number6 - Key_Number7 - Key_Number8 - Key_Number9 - Key_A - Key_B - Key_C - Key_D - Key_E - Key_F - Key_G - Key_H - Key_I - Key_J - Key_K - Key_L - Key_M - Key_N - Key_O - Key_P - Key_Q - Key_R - Key_S - Key_T - Key_U - Key_V - Key_W - Key_X - Key_Y - Key_Z - Key_Space - Key_Tab - Key_Return - Key_Escape - Key_Insert - Key_Delete - Key_Backspace - Key_Left - Key_Right - Key_Up - Key_Down - Key_Home - Key_End - Key_PageUp - Key_PageDown - -const inactiveTimer* = 0 - - -# ---------------------------------------------------------------------------------------- -# Widget Types 1/3 -# ---------------------------------------------------------------------------------------- - -type - # Window base type: - - Window* = ref object of RootObj - fDisposed: bool - fTitle: string - fVisible: bool - fWidth, fHeight: int - fClientWidth, fClientHeight: int - fX, fY: int - fControl: Control - fIconPath: string - fOnDispose: WindowDisposeProc - fOnResize: ResizeProc - fOnDropFiles: DropFilesProc - fOnKeyDown: WindowKeyProc - - # Control base type: - - Control* = ref object of RootObj - fDisposed: bool - fParentControl: Control # is nil or object of ContainerImpl - fParentWindow: Window # only set for top level widget - fIndex: int - fVisible: bool - fWidth, fHeight: int - fX, fY: int - fWidthMode: WidthMode - fHeightMode: HeightMode - fMinWidth, fMinHeight: int - fMaxWidth, fMaxHeight: int - fXScrollEnabled, fYScrollEnabled: bool - fXScrollPos, fYScrollPos: int - fScrollableWidth, fScrollableHeight: int - fFontFamily: string - fFontSize: int - fTextColor: Color - fBackgroundColor: Color - fUseDefaultFontFamily: bool - fUseDefaultFontSize: bool - fUseDefaultTextColor: bool - fUseDefaultBackgroundColor: bool - fCanvas: Canvas - fOnDispose: ControlDisposeProc - fOnDraw: DrawProc - fOnMouseButtonDown: MouseButtonProc - fOnMouseButtonUp: MouseButtonProc - fOnClick: ClickProc - # fOnMouseMove: MouseMoveProc - fOnKeyDown: ControlKeyProc - fOnTextChange: TextChangeProc - tag*: string - - # Drawing: - - Canvas* = ref object of RootObj - fWidth: int - fHeight: int - fFontFamily: string - fFontSize: int - fTextColor: Color - fLineColor: Color - fAreaColor: Color - - Image* = ref object of RootObj - fCanvas: Canvas - - # Window events: - - WindowDisposeEvent* = ref object - window*: Window - cancel*: bool - WindowDisposeProc* = proc(event: WindowDisposeEvent) - - ResizeEvent* = ref object - window*: Window - ResizeProc* = proc(event: ResizeEvent) - - DropFilesEvent* = ref object - window*: Window - files*: seq[string] - DropFilesProc* = proc(event: DropFilesEvent) - - WindowKeyEvent* = ref object - window*: Window - key*: Key - unicode*: int - character*: string # UTF-8 character - WindowKeyProc* = proc(event: WindowKeyEvent) - - # Control events: - - ControlDisposeEvent* = ref object - control*: Control - cancel*: bool - ControlDisposeProc* = proc(event: ControlDisposeEvent) - - DrawEvent* = ref object - control*: Control - DrawProc* = proc(event: DrawEvent) - - MouseButtonEvent* = ref object - control*: Control - button*: MouseButton - x*: int - y*: int - MouseButtonProc* = proc(event: MouseButtonEvent) - - ClickEvent* = ref object - control*: Control - ClickProc* = proc(event: ClickEvent) - - ControlKeyEvent* = ref object - control*: Control - key*: Key - unicode*: int - character*: string # UTF-8 character - cancel*: bool - ControlKeyProc* = proc(event: ControlKeyEvent) - - TextChangeEvent* = ref object - control*: Control - TextChangeProc* = proc(event: TextChangeEvent) - - # Other events: - - ErrorHandlerProc* = proc() - - TimerEvent* = ref object - timer*: Timer - data*: pointer - TimerProc* = proc(event: TimerEvent) - - -# Platform-specific extension of Window and Control: -include nigui_platform_types1 - - -# ---------------------------------------------------------------------------------------- -# Widget Types 2/3 -# ---------------------------------------------------------------------------------------- - -type - # Basic controls: - - Container* = ref object of ControlImpl - fFrame: Frame - fChildControls: seq[Control] - - Frame* = ref object of ControlImpl - fText: string - - Button* = ref object of ControlImpl - fText: string - - Label* = ref object of ControlImpl - fText: string - - TextBox* = ref object of ControlImpl - - TextArea* = ref object of ControlImpl - fWrap: bool - - -# Platform-specific extension of basic controls: -include nigui_platform_types2 - - -# ---------------------------------------------------------------------------------------- -# Widget Types 3/3 -# ---------------------------------------------------------------------------------------- - -type - LayoutContainer* = ref object of ContainerImpl - fLayout: Layout - fXAlign: XAlign - fYAlign: YAlign - fPadding: int - fSpacing: int - - -# ---------------------------------------------------------------------------------------- -# Global Variables -# ---------------------------------------------------------------------------------------- - -var quitOnLastWindowClose* = true -var clickMaxXYMove* = 20 - -# dummy type and object, needed to use get/set properties -type App = object -var app*: App - - -# ---------------------------------------------------------------------------------------- -# Global/App Procedures -# ---------------------------------------------------------------------------------------- - -proc init*(app: App) - -proc run*(app: App) - -proc quit*(app: App) - -proc processEvents*(app: App) - -proc sleep*(app: App, milliSeconds: float) - -proc errorHandler*(app: App): ErrorHandlerProc -proc `errorHandler=`*(app: App, errorHandler: ErrorHandlerProc) - -proc defaultBackgroundColor*(app: App): Color -proc `defaultBackgroundColor=`*(app: App, color: Color) - -proc defaultTextColor*(app: App): Color -proc `defaultTextColor=`*(app: App, color: Color) - -proc defaultFontFamily*(app: App): string -proc `defaultFontFamily=`*(app: App, fontFamily: string) - -proc defaultFontSize*(app: App): int -proc `defaultFontSize=`*(app: App, fontSize: int) - -proc rgb*(red, green, blue: byte, alpha: byte = 255): Color - - -# ---------------------------------------------------------------------------------------- -# Dialogs -# ---------------------------------------------------------------------------------------- - -proc alert*(window: Window, message: string, title = "Message") {.discardable.} - -type OpenFileDialog* = ref object - title*: string - directory*: string - multiple*: bool - files*: seq[string] - -proc newOpenFileDialog*(): OpenFileDialog - -method run*(dialog: OpenFileDialog) - -type SaveFileDialog* = ref object - title*: string - directory*: string - defaultExtension*: string - defaultName*: string - file*: string - -proc newSaveFileDialog*(): SaveFileDialog - -method run*(dialog: SaveFileDialog) - - -# ---------------------------------------------------------------------------------------- -# Timers -# ---------------------------------------------------------------------------------------- - -proc startTimer*(milliSeconds: int, timerProc: TimerProc, data: pointer = nil): Timer {.discardable.} - -proc startRepeatingTimer*(milliSeconds: int, timerProc: TimerProc, data: pointer = nil): Timer {.discardable.} - -proc stop*(timer: var Timer) - - -# ---------------------------------------------------------------------------------------- -# Canvas -# ---------------------------------------------------------------------------------------- - -proc newCanvas*(control: Control = nil): CanvasImpl - -method destroy*(canvas: Canvas) - -method width*(canvas: Canvas): int - -method height*(canvas: Canvas): int - -method fontFamily*(canvas: Canvas): string -method `fontFamily=`*(canvas: Canvas, fontFamily: string) - -method fontSize*(canvas: Canvas): int -method `fontSize=`*(canvas: Canvas, fontSize: int) - -method textColor*(canvas: Canvas): Color -method `textColor=`*(canvas: Canvas, color: Color) - -method lineColor*(canvas: Canvas): Color -method `lineColor=`*(canvas: Canvas, color: Color) - -method areaColor*(canvas: Canvas): Color -method `areaColor=`*(canvas: Canvas, color: Color) - -method drawText*(canvas: Canvas, text: string, x, y = 0) - -method drawTextCentered*(canvas: Canvas, text: string, x, y = 0, width, height = -1) - -method drawLine*(canvas: Canvas, x1, y1, x2, y2: int) - -method drawRectArea*(canvas: Canvas, x, y, width, height: int) - -method drawRectOutline*(canvas: Canvas, x, y, width, height: int) - -method fill*(canvas: Canvas) - -method drawImage*(canvas: Canvas, image: Image, x, y = 0, width, height = -1) - -method setPixel*(canvas: Canvas, x, y: int, color: Color) - -method getTextLineWidth*(canvas: Canvas, text: string): int - -method getTextLineHeight*(canvas: Canvas): int - -method getTextWidth*(canvas: Canvas, text: string): int - - -# ---------------------------------------------------------------------------------------- -# Image -# ---------------------------------------------------------------------------------------- - -proc newImage*(): Image - -method resize*(image: Image, width, height: int) - -method loadFromFile*(image: Image, filePath: string) - -method saveToPngFile*(image: Image, filePath: string) - -method saveToJpegFile*(image: Image, filePath: string, quality = 80) - -method width*(image: Image): int - -method height*(image: Image): int - -method canvas*(image: Image): Canvas - - -# ---------------------------------------------------------------------------------------- -# Window -# ---------------------------------------------------------------------------------------- - -proc newWindow*(title: string = nil): Window -## Constructor for a Window object. -## If the title is nil, it will be set to the application filename. - -proc init*(window: WindowImpl) -## Initialize a WindowImpl object -## Only needed for own constructors. - -proc dispose*(window: var Window) -proc dispose*(window: Window) - -proc disposed*(window: Window): bool - -method visible*(window: Window): bool -method `visible=`*(window: Window, visible: bool) - -method show*(window: Window) - -method showModal*(window: Window, parent: Window) - -method hide*(window: Window) - -method control*(window: Window): Control -method `control=`*(window: Window, control: Control) - -method add*(window: Window, control: Control) - -method title*(window: Window): string -method `title=`*(window: Window, title: string) - -method x*(window: Window): int -method `x=`*(window: Window, x: int) - -method y*(window: Window): int -method `y=`*(window: Window, y: int) - -method centerOnScreen*(window: Window) - -method width*(window: Window): int -method `width=`*(window: Window, width: int) - -method height*(window: Window): int -method `height=`*(window: Window, height: int) - -method clientWidth*(window: Window): int - -method clientHeight*(window: Window): int - -method iconPath*(window: Window): string -method `iconPath=`*(window: Window, iconPath: string) - -method handleDisposeEvent*(window: Window, event: WindowDisposeEvent) - -method handleResizeEvent*(window: Window, event: ResizeEvent) - -method handleKeyDownEvent*(window: Window, event: WindowKeyEvent) - -method handleDropFilesEvent*(window: Window, event: DropFilesEvent) - -method onDispose*(window: Window): WindowDisposeProc -method `onDispose=`*(window: Window, callback: WindowDisposeProc) - -method onResize*(window: Window): ResizeProc -method `onResize=`*(window: Window, callback: ResizeProc) - -method onDropFiles*(window: Window): DropFilesProc -method `onDropFiles=`*(window: Window, callback: DropFilesProc) - -method onKeyDown*(window: Window): WindowKeyProc -method `onKeyDown=`*(window: Window, callback: WindowKeyProc) - - -# ---------------------------------------------------------------------------------------- -# Control -# ---------------------------------------------------------------------------------------- - -proc newControl*(): Control - -proc init*(control: Control) -proc init*(control: ControlImpl) - -proc dispose*(control: var Control) -proc dispose*(control: Control) - -proc disposed*(control: Control): bool - -method visible*(control: Control): bool -method `visible=`*(control: Control, visible: bool) -method show*(control: Control) -method hide*(control: Control) - -# Allow the outside to walk over child widgets -method childControls*(control: Control): seq[Control] - -method parentControl*(control: Control): Control - -method parentWindow*(control: Control): WindowImpl - -method width*(control: Control): int -# Set the control's width to a fixed value (sets widthMode to fixed) -method `width=`*(control: Control, width: int) - -method height*(control: Control): int -# Set the control's height to a fixed value (sets heightMode to fixed) -method `height=`*(control: Control, height: int) - -method minWidth*(control: Control): int -method `minWidth=`*(control: Control, minWidth: int) - -method minHeight*(control: Control): int -method `minHeight=`*(control: Control, minHeight: int) - -method maxWidth*(control: Control): int -method `maxWidth=`*(control: Control, maxWidth: int) - -method maxHeight*(control: Control): int -method `maxHeight=`*(control: Control, maxHeight: int) - -# Set the control's width and height without changing widthMode or heightMode -method setSize*(control: Control, width, height: int) - -method x*(control: Control): int -method `x=`*(control: Control, x: int) - -method y*(control: Control): int -method `y=`*(control: Control, y: int) - -method setPosition*(control: Control, x, y: int) - -method naturalWidth*(control: Control): int - -method naturalHeight*(control: Control): int - -method wantedWidth*(control: Control): int - -method wantedHeight*(control: Control): int - -method focus*(control: Control) - -method getTextLineWidth*(control: Control, text: string): int - -method getTextLineHeight*(control: Control): int - -method getTextWidth*(control: Control, text: string): int - -method `widthMode=`*(control: Control, mode: WidthMode) -method widthMode*(control: Control): WidthMode - -method heightMode*(control: Control): HeightMode -method `heightMode=`*(control: Control, mode: HeightMode) - -method visibleWidth*(control: Control): int - -method visibleHeight*(control: Control): int - -method xScrollPos*(control: Control): int -method `xScrollPos=`*(control: Control, xScrollPos: int) - -method yScrollPos*(control: Control): int -method `yScrollPos=`*(control: Control, yScrollPos: int) - -method scrollableWidth*(control: Control): int -method `scrollableWidth=`*(control: Control, scrollableWidth: int) - -method scrollableHeight*(control: Control): int -method `scrollableHeight=`*(control: Control, scrollableHeight: int) - -method fontFamily*(control: Control): string -method `fontFamily=`*(control: Control, fontFamily: string) -method setFontFamily*(control: Control, fontFamily: string) -method resetFontFamily*(control: Control) - -method fontSize*(control: Control): int -method `fontSize=`*(control: Control, fontSize: int) -method setFontSize*(control: Control, fontSize: int) -method resetFontSize*(control: Control) - -method backgroundColor*(control: Control): Color -method `backgroundColor=`*(control: Control, color: Color) -method setBackgroundColor*(control: Control, color: Color) -method resetBackgroundColor*(control: Control) - -method textColor*(control: Control): Color -method `textColor=`*(control: Control, color: Color) -method setTextColor*(control: Control, color: Color) -method resetTextColor*(control: Control) - -method forceRedraw*(control: Control) - -method canvas*(control: Control): Canvas - -method handleDisposeEvent*(control: Control, event: ControlDisposeEvent) - -method handleDrawEvent*(control: Control, event: DrawEvent) - -method handleMouseButtonDownEvent*(control: Control, event: MouseButtonEvent) - -method handleMouseButtonUpEvent*(control: Control, event: MouseButtonEvent) - -method handleClickEvent*(control: Control, event: ClickEvent) - -method handleKeyDownEvent*(control: Control, event: ControlKeyEvent) - -method handleTextChangeEvent*(control: Control, event: TextChangeEvent) - -method onDispose*(control: Control): ControlDisposeProc -method `onDispose=`*(control: Control, callback: ControlDisposeProc) - -method onDraw*(control: Control): DrawProc -method `onDraw=`*(control: Control, callback: DrawProc) - -method onMouseButtonDown*(control: Control): MouseButtonProc -method `onMouseButtonDown=`*(control: Control, callback: MouseButtonProc) - -method onMouseButtonUp*(control: Control): MouseButtonProc -method `onMouseButtonUp=`*(control: Control, callback: MouseButtonProc) - -method onClick*(control: Control): ClickProc -method `onClick=`*(control: Control, callback: ClickProc) - -method onKeyDown*(control: Control): ControlKeyProc -method `onKeyDown=`*(control: Control, callback: ControlKeyProc) - -method onTextChange*(control: Control): TextChangeProc -method `onTextChange=`*(control: Control, callback: TextChangeProc) - - -# ---------------------------------------------------------------------------------------- -# Container -# ---------------------------------------------------------------------------------------- - -proc newContainer*(): Container - -proc init*(container: Container) -proc init*(container: ContainerImpl) - -method frame*(container: Container): Frame -method `frame=`*(container: Container, frame: Frame) - -method add*(container: Container, control: Control) -method remove*(container: Container, control: Control) - -method getPadding*(container: Container): Spacing - -method setInnerSize*(container: Container, width, height: int) - - -# ---------------------------------------------------------------------------------------- -# LayoutContainer -# ---------------------------------------------------------------------------------------- - -proc newLayoutContainer*(layout: Layout): LayoutContainer - -method layout*(container: LayoutContainer): Layout -method `layout=`*(container: LayoutContainer, layout: Layout) - -method xAlign*(container: LayoutContainer): XAlign -method `xAlign=`*(container: LayoutContainer, xAlign: XAlign) - -method yAlign*(container: LayoutContainer): YAlign -method `yAlign=`*(container: LayoutContainer, yAlign: YAlign) - -method padding*(container: LayoutContainer): int -method `padding=`*(container: LayoutContainer, padding: int) - -method spacing*(container: LayoutContainer): int -method `spacing=`*(container: LayoutContainer, spacing: int) - - -# ---------------------------------------------------------------------------------------- -# Frame -# ---------------------------------------------------------------------------------------- - -proc newFrame*(text = ""): Frame - -proc init*(frame: Frame) -proc init*(frame: NativeFrame) - -method text*(frame: Frame): string -method `text=`*(frame: Frame, text: string) - -method getPadding*(frame: Frame): Spacing - - -# ---------------------------------------------------------------------------------------- -# Button -# ---------------------------------------------------------------------------------------- - -proc newButton*(text = ""): Button - -proc init*(button: Button) -proc init*(button: NativeButton) - -method text*(button: Button): string -method `text=`*(button: Button, text: string) - - -# ---------------------------------------------------------------------------------------- -# Label -# ---------------------------------------------------------------------------------------- - -proc newLabel*(text = ""): Label - -proc init*(label: Label) -proc init*(label: NativeLabel) - -method text*(label: Label): string -method `text=`*(label: Label, text: string) - - -# ---------------------------------------------------------------------------------------- -# TextBox -# ---------------------------------------------------------------------------------------- - -proc newTextBox*(text = ""): TextBox - -proc init*(textBox: TextBox) -proc init*(textBox: NativeTextBox) - -method text*(textBox: TextBox): string -method `text=`*(textBox: TextBox, text: string) - - -# ---------------------------------------------------------------------------------------- -# TextArea -# ---------------------------------------------------------------------------------------- - -proc newTextArea*(text = ""): TextArea - -proc init*(textArea: TextArea) -proc init*(textArea: NativeTextArea) - -method text*(textArea: TextArea): string -method `text=`*(textArea: TextArea, text: string) -method addText*(textArea: TextArea, text: string) -method addLine*(textArea: TextArea, text = "") - -method scrollToBottom*(textArea: TextArea) - -method wrap*(textArea: TextArea): bool -method `wrap=`*(textArea: TextArea, wrap: bool) - - -# ---------------------------------------------------------------------------------------- -# Private Procedures Predeclaration -# ---------------------------------------------------------------------------------------- - -proc raiseError(msg: string, showAlert = true, title = "NiGui Error") - -proc handleException() - -proc runMainLoop() - -proc init(window: Window) - -method destroy(window: Window) - -proc triggerRelayout(window: Window) - -method destroy(control: Control) - -proc triggerRelayout(control: Control) - -proc triggerRelayoutIfModeIsAuto(control: Control) - -method relayout(control: Control, availableWidth, availableHeight: int) - -method realignChildControls(control: Control) - -method setControlPosition(container: Container, control: Control, x, y: int) - -proc countLines(s: string): int - - -# ======================================================================================== -# -# Implementation -# -# ======================================================================================== - -import math -import os -import strutils -import times - - -# ---------------------------------------------------------------------------------------- -# Global Variables -# ---------------------------------------------------------------------------------------- - -var fErrorHandler: ErrorHandlerProc = nil -var windowList: seq[Window] = @[] -var fScrollbarSize = -1 - -# Default style: -var fDefaultBackgroundColor: Color # initialized by platform-specific init() -var fDefaultTextColor: Color # initialized by platform-specific init() -var fDefaultFontFamily = "" -var fDefaultFontSize = 15 - - -# ---------------------------------------------------------------------------------------- -# Global/App Procedures -# ---------------------------------------------------------------------------------------- - -proc raiseError(msg: string, showAlert = true, title = "NiGui Error") = - if showAlert: - alert(nil, msg & "\n\n" & getStackTrace(), title) - raise newException(Exception, msg) - -proc handleException() = - if fErrorHandler == nil: - raiseError(getCurrentExceptionMsg(), true, "Unhandled Exception") - else: - fErrorHandler() - -proc rgb(red, green, blue: byte, alpha: byte = 255): Color = - result.red = red - result.green = green - result.blue = blue - result.alpha = alpha - -proc countLines(s: string): int = strutils.countLines(s) + 1 - -proc sleep(app: App, milliSeconds: float) = - let t = epochTime() + milliSeconds / 1000 - while epochTime() < t: - app.processEvents() - os.sleep(20) - -proc run(app: App) = - while true: - try: - runMainLoop() - break - except: - handleException() - -proc quit(app: App) = quit() - -proc errorHandler(app: App): ErrorHandlerProc = fErrorHandler - -proc `errorHandler=`(app: App, errorHandler: ErrorHandlerProc) = fErrorHandler = errorHandler - -proc defaultBackgroundColor(app: App): Color = fDefaultBackgroundColor - -proc updateBackgroundColor(control: Control) = - if control.fUseDefaultBackgroundColor and control.backgroundColor != fDefaultBackgroundColor: - control.setBackgroundColor(fDefaultBackgroundColor) - for child in control.childControls: - child.updateBackgroundColor() - -proc `defaultBackgroundColor=`(app: App, color: Color) = - fDefaultBackgroundColor = color - for window in windowList: - let control = window.control - if control != nil: - control.updateBackgroundColor() - -proc defaultTextColor(app: App): Color = fDefaultTextColor - -proc updateTextColor(control: Control) = - if control.fUseDefaultTextColor and control.textColor != fDefaultTextColor: - control.setTextColor(fDefaultTextColor) - for child in control.childControls: - child.updateTextColor() - -proc `defaultTextColor=`(app: App, color: Color) = - fDefaultTextColor = color - for window in windowList: - let control = window.control - if control != nil: - control.updateTextColor() - -proc defaultFontFamily(app: App): string = fDefaultFontFamily - -proc updateFontFamily(control: Control) = - if control.fUseDefaultFontFamily and control.fontFamily != fDefaultFontFamily: - control.setFontFamily(fDefaultFontFamily) - for child in control.childControls: - child.updateFontFamily() - -proc `defaultFontFamily=`(app: App, fontFamily: string) = - fDefaultFontFamily = fontFamily - for window in windowList: - let control = window.control - if control != nil: - control.updateFontFamily() - -proc defaultFontSize(app: App): int = fDefaultFontSize - -proc updateFontSize(control: Control) = - if control.fUseDefaultFontSize and control.fontSize != fDefaultFontSize: - control.setFontSize(fDefaultFontSize) - for child in control.childControls: - child.updateFontSize() - -proc `defaultFontSize=`(app: App, fontSize: int) = - fDefaultFontSize = fontSize - for window in windowList: - let control = window.control - if control != nil: - control.updateFontSize() - -proc newOpenFileDialog(): OpenFileDialog = - result = new OpenFileDialog - result.title = "Open File" - result.directory = getCurrentDir() - result.files = @[] - -proc newSaveFileDialog(): SaveFileDialog = - result = new SaveFileDialog - result.title = "Save File" - result.directory = getCurrentDir() - result.defaultExtension = "" - result.defaultName = "" - result.file = "" - - -# ---------------------------------------------------------------------------------------- -# Canvas -# ---------------------------------------------------------------------------------------- - -proc newCanvas(control: Control = nil): CanvasImpl = - result = new CanvasImpl - result.fLineColor = rgb(0, 0, 0) - result.fAreaColor = rgb(0, 0, 0) - if control == nil: - result.fFontFamily = app.defaultFontFamily - result.fFontSize = app.defaultFontSize - result.fTextColor = app.defaultTextColor - else: - result.fFontFamily = control.fontFamily - result.fFontSize = control.fontSize - result.fTextColor = control.textColor - result.fWidth = control.width - result.fHeight = control.height - control.fCanvas = result - -method destroy(canvas: Canvas) = discard - -method width(canvas: Canvas): int = canvas.fWidth - -method height(canvas: Canvas): int= canvas.fHeight - -method fontFamily(canvas: Canvas): string = canvas.fFontFamily - -method `fontFamily=`(canvas: Canvas, fontFamily: string) = canvas.fFontFamily = fontFamily - -method fontSize(canvas: Canvas): int = canvas.fFontSize - -method `fontSize=`(canvas: Canvas, fontSize: int) = canvas.fFontSize = fontSize - -method textColor(canvas: Canvas): Color = canvas.fTextColor - -method `textColor=`(canvas: Canvas, color: Color) = canvas.fTextColor = color - -method lineColor(canvas: Canvas): Color = canvas.fLineColor - -method `lineColor=`(canvas: Canvas, color: Color) = canvas.fLineColor = color - -method areaColor(canvas: Canvas): Color = canvas.fAreaColor - -method `areaColor=`(canvas: Canvas, color: Color) = canvas.fAreaColor = color - -method getTextLineWidth(canvas: Canvas, text: string): int = - result = text.len * 7 - # should be overrriden by CanvasImpl - -method getTextLineHeight(canvas: Canvas): int = - result = 20 - # should be overrriden by CanvasImpl - -method getTextWidth(canvas: Canvas, text: string): int = - result = 0 - for line in text.splitLines: - result = max(result, canvas.getTextLineWidth(line)) - -method drawTextCentered(canvas: Canvas, text: string, x, y = 0, width, height = -1) = - var w = width - if w == -1: - w = canvas.width - var h = height - if h == -1: - h = canvas.height - let rx = x + (w - canvas.getTextWidth(text)) div 2 - let ry = y + (h - canvas.getTextLineHeight()) div 2 - canvas.drawText(text, rx, ry) - -method fill(canvas: Canvas) = canvas.drawRectArea(0, 0, canvas.width, canvas.height) - - -# ---------------------------------------------------------------------------------------- -# Image -# ---------------------------------------------------------------------------------------- - -proc newImage(): Image = - result = new ImageImpl - result.fCanvas = newCanvas() - -method width(image: Image): int = image.canvas.width - -method height(image: Image): int = image.canvas.height - -method canvas(image: Image): Canvas = image.fCanvas - - -# ---------------------------------------------------------------------------------------- -# Window -# ---------------------------------------------------------------------------------------- - -proc newWindow(title: string = nil): Window = - result = new WindowImpl - result.WindowImpl.init() - if title != nil: - result.title = title - - -proc init(window: Window) = - window.fVisible = false - window.fWidth = 640 # do not trigger resize - window.height = 480 # trigger resize - window.fX = -1 # window will be centered on screen - window.fY = -1 - window.title = getAppFilename().extractFilename().changeFileExt("") - var defaultIconPath = getAppFilename().changeFileExt("") & ".png" - if defaultIconPath.fileExists(): - window.iconPath = defaultIconPath - windowList.add(window) - window.triggerRelayout() - - -method destroy(window: Window) = - if window.fControl != nil: - window.fControl.destroy() - # should be extended by WindowImpl - -proc disposeInner(window: Window): bool = - var event = new WindowDisposeEvent - event.window = window - window.handleDisposeEvent(event) - if event.cancel: - return false - window.destroy() - let i = windowList.find(window) - windowList.delete(i) - if quitOnLastWindowClose and windowList.len == 0: - quit() - window.fDisposed = true - return true - -proc dispose(window: var Window) = - if window.disposeInner(): - window = nil - -proc dispose(window: Window) = - discard window.disposeInner() - -proc disposed(window: Window): bool = window == nil or window.fDisposed - -method title(window: Window): string = window.fTitle - -method `title=`(window: Window, title: string) = window.fTitle = title - -method control(window: Window): Control = window.fControl - -method `control=`(window: Window, control: Control) = - window.fControl = control - control.fParentWindow = window - # should be extended by WindowImpl - -method add(window: Window, control: Control) = - if window.control != nil: - raiseError("Window can have only one control.") - window.control = control - -method visible(window: Window): bool = window.fVisible - -method `visible=`(window: Window, visible: bool) = - window.fVisible = visible - if window.x == -1 or window.y == -1: - window.centerOnScreen() - -method show(window: Window) = window.visible = true - -method showModal(window: Window, parent: Window) = - window.visible = true - # should be extended by WindowImpl - -method hide(window: Window) = window.visible = false - -method x(window: Window): int = window.fX - -method `x=`(window: Window, x: int) = - window.fX = x - # should be extended by WindowImpl - -method y(window: Window): int = window.fY - -method `y=`(window: Window, y: int) = - window.fY = y - # should be extended by WindowImpl - -method centerOnScreen(window: Window) = - discard # has to be implemented in WindowImpl - -method width(window: Window): int = window.fWidth - -method height(window: Window): int = window.fHeight - -method `width=`(window: Window, width: int) = - window.fWidth = width - window.triggerRelayout() - var event = new ResizeEvent - event.window = window - window.handleResizeEvent(event) - -method `height=`(window: Window, height: int) = - window.fHeight = height - window.triggerRelayout() - var event = new ResizeEvent - event.window = window - window.handleResizeEvent(event) - -method clientWidth(window: Window): int = window.fClientWidth - -method clientHeight(window: Window): int = window.fClientHeight - -proc triggerRelayout(window: Window) = - if window.control == nil: - return - # echo "" - # echo "WindowImpl:triggerRelayout()" - # echo "window size: " & $window.clientWidth & ", " & $window.clientHeight - window.control.relayout(window.clientWidth, window.clientHeight) - -method iconPath(window: Window): string = window.fIconPath - -method `iconPath=`(window: Window, iconPath: string) = - window.fIconPath = iconPath - # should be extended by WindowImpl - -method handleDisposeEvent(window: Window, event: WindowDisposeEvent) = - # can be overriden by custom window - let callback = window.onDispose - if callback != nil: - callback(event) - -method handleResizeEvent(window: Window, event: ResizeEvent) = - # can be overriden by custom window - let callback = window.onResize - if callback != nil: - callback(event) - -method handleDropFilesEvent(window: Window, event: DropFilesEvent) = - # can be overriden by custom window - let callback = window.onDropFiles - if callback != nil: - callback(event) - -method handleKeyDownEvent(window: Window, event: WindowKeyEvent) = - # can be overriden by custom window - let callback = window.onKeyDown - if callback != nil: - callback(event) - -method onDispose(window: Window): WindowDisposeProc = window.fOnDispose -method `onDispose=`(window: Window, callback: WindowDisposeProc) = window.fOnDispose = callback - -method onResize(window: Window): ResizeProc = window.fOnResize -method `onResize=`(window: Window, callback: ResizeProc) = window.fOnResize = callback - -method onDropFiles(window: Window): DropFilesProc = window.fOnDropFiles -method `onDropFiles=`(window: Window, callback: DropFilesProc) = window.fOnDropFiles = callback - -method onKeyDown(window: Window): WindowKeyProc = window.fOnKeyDown -method `onKeyDown=`(window: Window, callback: WindowKeyProc) = window.fOnKeyDown = callback - - - -# ---------------------------------------------------------------------------------------- -# Control -# ---------------------------------------------------------------------------------------- - -proc newControl(): Control = - result = new ControlImpl - result.ControlImpl.init() - -proc init(control: Control) = - control.tag = "" - control.fWidthMode = WidthMode_Expand - control.fHeightMode = HeightMode_Expand - control.fScrollableWidth = -1 - control.fScrollableHeight = -1 - control.resetFontFamily() - control.resetFontSize() - control.resetTextColor() - control.resetBackgroundColor() - control.show() - # should be extended by WindowImpl - -method destroy(control: Control) = - discard # nothing to do here - # should be extended by WindowImpl - -proc dispose(control: var Control) = - control.destroy() - control = nil - -proc dispose(control: Control) = - control.destroy() - control.fDisposed = true - -proc disposed(control: Control): bool = control == nil or control.fDisposed - -method visible(control: Control): bool = control.fVisible - -method `visible=`(control: Control, visible: bool) = - control.fVisible = visible - control.triggerRelayout() - # should be extended by WindowImpl - -method show(control: Control) = control.visible = true - -method hide(control: Control) = control.visible = false - -method width(control: Control): int = control.fWidth - -method height(control: Control): int = control.fHeight - -method `width=`(control: Control, width: int) = - control.setSize(width, control.fHeight) - control.widthMode = WidthMode_Static - -method `height=`(control: Control, height: int) = - control.setSize(control.fWidth, height) - control.heightMode = HeightMode_Static - -method minWidth(control: Control): int = control.fMinWidth - -method `minWidth=`(control: Control, minWidth: int) = - control.fMinWidth = minWidth - control.triggerRelayout() - -method minHeight(control: Control): int = control.fMinHeight - -method `minHeight=`(control: Control, minHeight: int) = - control.fMinHeight = minHeight - control.triggerRelayout() - -method maxWidth(control: Control): int = control.fMaxWidth - -method `maxWidth=`(control: Control, maxWidth: int) = - control.fMaxWidth = maxWidth - control.triggerRelayout() - -method maxHeight(control: Control): int = control.fMaxHeight - -method `maxHeight=`(control: Control, maxHeight: int) = - control.fMaxHeight = maxHeight - control.triggerRelayout() - -method setSize(control: Control, width, height: int) = - control.fWidth = width - control.fHeight = height - if control.canvas != nil: - control.canvas.fWidth = width - control.canvas.fHeight = height - control.realignChildControls() - # should be extended by ControlImpl - -method setSize(control: ControlImpl, width, height: int) # required pre declaration - -method `x=`(control: Control, x: int) = - if control.fParentControl == nil: - raiseError("Control cannot be moved, when it is not inside a container.") - cast[Container](control.fParentControl).setControlPosition(control, x, control.y) - -method x(control: Control): int = control.fX - -method `y=`(control: Control, y: int) = - if control.fParentControl == nil: - raiseError("Control cannot be moved, when it is not inside a container.") - cast[Container](control.fParentControl).setControlPosition(control, control.x, y) - -method y(control: Control): int = control.fY - -method setPosition(control: Control, x, y: int) = - control.fX = x - control.fY = y - # should be extended by ControlImpl - -method naturalWidth(control: Control): int = control.width - -method naturalHeight(control: Control): int = control.height - -method wantedWidth(control: Control): int = - if control.widthMode == WidthMode_Static: - result = control.width - else: - result = control.naturalWidth - if result != -1 and control.minWidth > result: - result = control.minWidth - if control.maxWidth > 0 and control.maxWidth < result: - result = control.maxWidth - -method wantedHeight(control: Control): int = - if control.heightMode == HeightMode_Static: - result = control.height - else: - result = control.naturalHeight - if result != -1 and control.minHeight > result: - result = control.minHeight - if control.maxHeight > 0 and control.maxHeight < result: - result = control.maxHeight - -method `widthMode=`(control: Control, mode: WidthMode) = - control.fWidthMode = mode - control.triggerRelayout() - -method `heightMode=`(control: Control, mode: HeightMode) = - control.fHeightMode = mode - control.triggerRelayout() - -method widthMode(control: Control): WidthMode = control.fWidthMode - -method heightMode(control: Control): HeightMode = control.fHeightMode - -method childControls(control: Control): seq[Control] = @[] - -method parentControl(control: Control): Control = control.fParentControl - -method parentWindow(control: Control): WindowImpl = - if control.fParentControl == nil: - result = cast[WindowImpl](control.fParentWindow) - else: - result = control.parentControl.parentWindow - -proc triggerRelayout(control: Control) = - var con = control - while con.parentControl != nil: - con = con.parentControl - if con.parentWindow != nil: - con.parentWindow.triggerRelayout() - if control.parentControl != nil: - control.parentControl.realignChildControls() - control.realignChildControls() - -proc triggerRelayoutIfModeIsAuto(control: Control) = - if control.widthMode == WidthMode_Auto or control.heightMode == HeightMode_Auto: - control.triggerRelayout() - -method relayout(control: Control, availableWidth, availableHeight: int) = - # echo "" - # echo control.tag & ".relayout(): " - # echo " available size: " & $availableWidth & ", " & $availableHeight - var width = control.width - var height = control.height - var sizeChanged = false - if control.widthMode == WidthMode_Auto: - let naturalWidth = control.naturalWidth - # echo " naturalWidth: " & $naturalWidth - if naturalWidth == -1: - width = availableWidth - else: - width = min(availableWidth, naturalWidth) - sizeChanged = true - elif control.widthMode in {WidthMode_Expand, WidthMode_Fill}: - width = availableWidth - sizeChanged = true - if control.heightMode == HeightMode_Auto: - let naturalHeight = control.naturalHeight - # echo " naturalHeight: " & $naturalHeight - if naturalHeight == -1: - height = availableHeight - else: - height = min(availableHeight, naturalHeight) - sizeChanged = true - elif control.heightMode in {HeightMode_Expand, HeightMode_Fill}: - height = availableHeight - sizeChanged = true - if sizeChanged: - # echo " new size: " & $width & ", " & $height - control.setSize(width, height) - -method realignChildControls(control: Control) = discard - -method focus(control: Control) = - discard - # should be overrriden by ControlImpl - -method getTextLineWidth(control: Control, text: string): int = - result = text.len * 7 - # should be overrriden by ControlImpl - -method getTextLineHeight(control: Control): int = - result = 20 - # should be overrriden by ControlImpl - -method getTextWidth(control: Control, text: string): int = - result = 0 - for line in text.splitLines: - result = max(result, control.getTextLineWidth(line)) - -method visibleWidth(control: Control): int = - result = control.width - if control.fXScrollEnabled: - result.dec(fScrollbarSize) - -method visibleHeight(control: Control): int = - result = control.height - if control.fYScrollEnabled: - result.dec(fScrollbarSize) - -method xScrollPos(control: Control): int = control.fXScrollPos - -method `xScrollPos=`(control: Control, xScrollPos: int) = - control.fXScrollPos = xScrollPos - -method yScrollPos(control: Control): int = control.fYScrollPos - -method `yScrollPos=`(control: Control, yScrollPos: int) = - control.fYScrollPos = yScrollPos - -method scrollableWidth(control: Control): int = control.fScrollableWidth - -method `scrollableWidth=`(control: Control, scrollableWidth: int) = - control.fScrollableWidth = scrollableWidth - -method scrollableHeight(control: Control): int = control.fScrollableHeight - -method `scrollableHeight=`(control: Control, scrollableHeight: int) = - control.fScrollableHeight = scrollableHeight - -method fontFamily(control: Control): string = control.fFontFamily - -method `fontFamily=`(control: Control, fontFamily: string) = - control.setFontFamily(fontFamily) - control.fUseDefaultFontFamily = false - -method setFontFamily(control: Control, fontFamily: string) = - control.fFontFamily = fontFamily - control.triggerRelayoutIfModeIsAuto() - # should be extended by ControlImpl - -method resetFontFamily(control: Control) = - control.setFontFamily(fDefaultFontFamily) - control.fUseDefaultFontFamily = true - -method fontSize(control: Control): int = control.fFontSize - -method `fontSize=`(control: Control, fontSize: int) = - control.setFontSize(fontSize) - control.fUseDefaultFontSize = false - -method setFontSize(control: Control, fontSize: int) = - control.fFontSize = fontSize - control.triggerRelayoutIfModeIsAuto() - # should be extended by ControlImpl - -method resetFontSize(control: Control) = - control.setFontSize(fDefaultFontSize) - control.fUseDefaultFontSize = true - -method backgroundColor(control: Control): Color = control.fBackgroundColor - -method `backgroundColor=`(control: Control, color: Color) = - control.setBackgroundColor(color) - control.fUseDefaultBackgroundColor = false - -method setBackgroundColor(control: Control, color: Color) = - control.fBackgroundColor = color - control.forceRedraw() - # should be extended by ControlImpl - -method resetBackgroundColor(control: Control) = - control.setBackgroundColor(fDefaultBackgroundColor) - control.fUseDefaultBackgroundColor = true - -method textColor(control: Control): Color = control.fTextColor - -method `textColor=`(control: Control, color: Color) = - control.setTextColor(color) - control.fUseDefaultTextColor = false - -method setTextColor(control: Control, color: Color) = - control.fTextColor = color - control.forceRedraw() - # should be extended by ControlImpl - -method resetTextColor*(control: Control) = - control.setTextColor(fDefaultTextColor) - control.fUseDefaultTextColor = true - -method forceRedraw(control: Control) = - discard - # should be implemented by ControlImpl - -method canvas(control: Control): Canvas = control.fCanvas - -method handleDisposeEvent(control: Control, event: ControlDisposeEvent) = - # can be overriden by custom window - let callback = control.onDispose - if callback != nil: - callback(event) - -method handleDrawEvent(control: Control, event: DrawEvent) = - # can be implemented by custom control - let callback = control.onDraw - if callback != nil: - callback(event) - -method handleMouseButtonDownEvent(control: Control, event: MouseButtonEvent) = - # can be implemented by custom control - let callback = control.onMouseButtonDown - if callback != nil: - callback(event) - -method handleMouseButtonUpEvent(control: Control, event: MouseButtonEvent) = - # can be implemented by custom control - let callback = control.onMouseButtonUp - if callback != nil: - callback(event) - -method handleClickEvent(control: Control, event: ClickEvent) = - # can be overridden by custom button - let callback = control.onClick - if callback != nil: - callback(event) - -method handleKeyDownEvent(control: Control, event: ControlKeyEvent) = - # can be implemented by custom control - let callback = control.onKeyDown - if callback != nil: - callback(event) - -method handleTextChangeEvent(control: Control, event: TextChangeEvent) = - # can be implemented by custom control - let callback = control.onTextChange - if callback != nil: - callback(event) - -method onDispose(control: Control): ControlDisposeProc = control.fOnDispose -method `onDispose=`(control: Control, callback: ControlDisposeProc) = control.fOnDispose = callback - -method onDraw(control: Control): DrawProc = control.fOnDraw -method `onDraw=`(control: Control, callback: DrawProc) = control.fOnDraw = callback - -method onMouseButtonDown(control: Control): MouseButtonProc = control.fOnMouseButtonDown -method `onMouseButtonDown=`(control: Control, callback: MouseButtonProc) = control.fOnMouseButtonDown = callback - -method onMouseButtonUp(control: Control): MouseButtonProc = control.fOnMouseButtonUp -method `onMouseButtonUp=`(control: Control, callback: MouseButtonProc) = control.fOnMouseButtonUp = callback - -method onClick(control: Control): ClickProc = control.fOnClick -method `onClick=`(control: Control, callback: ClickProc) = control.fOnClick = callback - -method onKeyDown(control: Control): ControlKeyProc = control.fOnKeyDown -method `onKeyDown=`(control: Control, callback: ControlKeyProc) = control.fOnKeyDown = callback - -method onTextChange(control: Control): TextChangeProc = control.fOnTextChange -method `onTextChange=`(control: Control, callback: TextChangeProc) = control.fOnTextChange = callback - - -# ---------------------------------------------------------------------------------------- -# Container -# ---------------------------------------------------------------------------------------- - -proc newContainer(): Container = - result = new ContainerImpl - result.ContainerImpl.init() - -proc init(container: Container) = - container.fChildControls = @[] - container.ControlImpl.init() - container.fWidthMode = WidthMode_Auto - container.fHeightMode = HeightMode_Auto - -method frame(container: Container): Frame = container.fFrame - -method `frame=`(container: Container, frame: Frame) = - if frame.fParentControl != nil: - raiseError("Frame can be assigned only to one container.") - container.fFrame = frame - if frame != nil: - frame.fParentControl = container - container.tag = frame.tag - container.triggerRelayout() - if container.frame != nil: - container.frame.setSize(container.width, container.height) - # should be extended by NativeFrame - -method setSize(container: Container, width, height: int) = - procCall container.ControlImpl.setSize(width, height) - if container.frame != nil: - container.frame.setSize(width, height) - -method childControls(container: Container): seq[Control] = container.fChildControls - -method add(container: Container, control: Control) = - if control.fParentControl != nil: - raiseError("Control can be added only to one container.") - container.fChildControls.add(control) - control.fParentControl = container - control.fIndex = 0 - container.triggerRelayout() - -method remove(container: Container, control: Control) = - discard - # if container != control.fParentControl: - # raiseError("control can not be removed because it is not member of the container") - # else: - # let startIndex = control.fIndex - # container.childControls.del(control.fIndex) - # for i in startIndex..container.childControls.high: - # container.childControl[i].fIndex = i - # control.parentControl = nil - -method setControlPosition(container: Container, control: Control, x, y: int) = - control.setPosition(x, y) - container.triggerRelayout() - -method minWidth(container: Container): int = - result = 0 - for control in container.childControls: - if not control.visible: - continue - result = max(result, control.x + control.minWidth) - let padding = container.getPadding() - result.inc(padding.left) - result.inc(padding.right) - result = max(result, container.fMinWidth) - -method minHeight(container: Container): int = - result = 0 - for control in container.childControls: - if not control.visible: - continue - result = max(result, control.y + control.minHeight) - let padding = container.getPadding() - result.inc(padding.top) - result.inc(padding.bottom) - result = max(result, container.fMinHeight) - -method naturalWidth(container: Container): int = - if container.widthMode == WidthMode_Static: - return container.width - if container.widthMode == WidthMode_Expand: - return -1 - result = 0 - for control in container.childControls: - if not control.visible: - continue - if control.widthMode == WidthMode_Expand: - return -1 - result = max(result, control.x + control.wantedWidth) - let padding = container.getPadding() - result.inc(padding.left) - result.inc(padding.right) - if container.frame != nil and container.frame.visible: - result = max(result, container.frame.naturalWidth) - -method naturalHeight(container: Container): int = - if container.heightMode == HeightMode_Static: - return container.height - if container.heightMode == HeightMode_Expand: - return -1 - result = 0 - for control in container.childControls: - if not control.visible: - continue - if control.heightMode == HeightMode_Expand: - return -1 - result = max(result, control.y + control.wantedHeight) - let padding = container.getPadding() - result.inc(padding.top) - result.inc(padding.bottom) - -method getPadding(container: Container): Spacing = - if container.frame != nil and container.frame.visible: - result = container.frame.getPadding() - -method setInnerSize(container: Container, width, height: int) = discard - # should be extended by ContainerImpl - -method updateInnerSize(container: Container, pInnerWidth, pInnerHeight: int) {.base.} = - let padding = container.getPadding() - let clientWidth = container.width - padding.left - padding.right - let clientHeight = container.height - padding.top - padding.bottom - var innerWidth = pInnerWidth - var innerHeight = pInnerHeight - - discard """ container.xScrollEnabled = innerWidth > clientWidth - - if container.xScrollEnabled and innerHeight + 20 > clientHeight: - innerHeight.inc(20) # Space for scrollbar - container.yScrollEnabled = true - else: - container.yScrollEnabled = innerHeight > clientHeight - if container.yScrollEnabled and innerWidth + 20 > clientWidth: - innerWidth.inc(20) # Space for scrollbar - container.xScrollEnabled = true """ - - # TODO: rework - - innerWidth = max(innerWidth, clientWidth) - innerHeight = max(innerHeight, clientHeight) - - container.scrollableWidth = innerWidth - container.scrollableHeight = innerHeight - container.setInnerSize(innerWidth, innerHeight) - -method realignChildControls(container: Container) = - let padding = container.getPadding() - var innerWidth = container.wantedWidth - var innerHeight = container.wantedHeight - if innerWidth == -1: - innerWidth = container.width - if innerHeight == -1: - innerHeight = container.height - container.updateInnerSize(innerWidth - padding.left - padding.right, innerHeight - padding.top - padding.bottom) - -method `onDraw=`(container: ContainerImpl, callback: DrawProc) = raiseError("ContainerImpl does not allow onDraw.") - - -# ---------------------------------------------------------------------------------------- -# LayoutContainer -# ---------------------------------------------------------------------------------------- - -proc newLayoutContainer(layout: Layout): LayoutContainer = - result = new LayoutContainer - result.init() - result.layout = layout - result.xAlign = XAlign_Left - result.yAlign = YAlign_Top - result.spacing = 4 - result.padding = 2 - -method naturalWidth(container: LayoutContainer): int = - # echo container.tag & ".naturalWidth" - if container.widthMode == WidthMode_Static: - return container.width - if container.widthMode == WidthMode_Expand: - return -1 - result = 0 - for control in container.childControls: - if not control.visible: - continue - if control.widthMode == WidthMode_Expand or control.wantedWidth == -1: - return -1 - if container.layout == Layout_Horizontal: - result.inc(control.wantedWidth) - else: - result = max(result, control.wantedWidth) - let padding = container.getPadding() - result.inc(padding.left) - result.inc(padding.right) - result.inc(container.padding * 2) - if container.layout == Layout_Horizontal and container.childControls.len > 1: - result.inc(container.spacing * (container.childControls.len - 1)) - if container.frame != nil and container.frame.visible: - result = max(result, container.frame.naturalWidth) - -method naturalHeight(container: LayoutContainer): int = - if container.heightMode == HeightMode_Static: - return container.height - if container.heightMode == HeightMode_Expand: - return -1 - result = 0 - for control in container.childControls: - if not control.visible: - continue - if control.heightMode == HeightMode_Expand or control.wantedHeight == -1: - return -1 - if container.layout == Layout_Vertical: - result.inc(control.wantedHeight) - else: - result = max(result, control.wantedHeight) - let padding = container.getPadding() - result.inc(padding.top) - result.inc(padding.bottom) - result.inc(container.padding * 2) - if container.layout == Layout_Vertical and container.childControls.len > 1: - result.inc(container.spacing * (container.childControls.len - 1)) - -method minWidth(container: LayoutContainer): int = - result = 0 - for control in container.childControls: - if not control.visible: - continue - if container.layout == Layout_Horizontal: - result.inc(control.minWidth) - else: - result = max(result, control.minWidth) - let padding = container.getPadding() - result.inc(padding.left) - result.inc(padding.right) - result.inc(container.padding * 2) - if container.layout == Layout_Horizontal and container.childControls.len > 1: - result.inc(container.spacing * (container.childControls.len - 1)) - result = max(result, container.fMinWidth) - -method minHeight(container: LayoutContainer): int = - result = 0 - for control in container.childControls: - if not control.visible: - continue - if container.layout == Layout_Vertical: - result.inc(control.minHeight) - else: - result = max(result, control.minHeight) - let padding = container.getPadding() - result.inc(padding.top) - result.inc(padding.bottom) - result.inc(container.padding * 2) - if container.layout == Layout_Vertical and container.childControls.len > 1: - result.inc(container.spacing * (container.childControls.len - 1)) - result = max(result, container.fMinHeight) - -method setControlPosition(container: LayoutContainer, control: Control, x, y: int) = - raiseError("Controls inside a LayoutContainer cannot be moved manually.") - -method layout(container: LayoutContainer): Layout = container.fLayout - -method `layout=`(container: LayoutContainer, layout: Layout) = - container.fLayout = layout - container.triggerRelayout() - -method xAlign(container: LayoutContainer): XAlign = container.fXAlign - -method `xAlign=`(container: LayoutContainer, xAlign: XAlign) = - container.fXAlign = xAlign - container.realignChildControls() - -method yAlign(container: LayoutContainer): YAlign = container.fYAlign - -method `yAlign=`(container: LayoutContainer, yAlign: YAlign) = - container.fYAlign = yAlign - container.realignChildControls() - -method padding(container: LayoutContainer): int = container.fPadding - -method `padding=`(container: LayoutContainer, padding: int) = - container.fPadding = padding - container.triggerRelayout() - -method spacing(container: LayoutContainer): int = container.fSpacing - -method `spacing=`(container: LayoutContainer, spacing: int) = - container.fSpacing = spacing - container.triggerRelayout() - -method realignChildControls(container: LayoutContainer) = - # echo "" - # echo container.tag & ".realignChildControls()" - if container.fChildControls.len == 0: - return - # echo " container size: " & $container.width & ", " & $container.height - let padding = container.getPadding() - let clientWidth = container.width - padding.left - padding.right - let clientHeight = container.height - padding.top - padding.bottom - # echo " client size: " & $clientWidth & ", " & $clientHeight - var minInnerWidth = 0 - var minInnerHeight = 0 - var expandWidthCount = 0 - var expandHeightCount = 0 - - # Calculate minimum needed size: - for control in container.fChildControls: - if not control.visible: - continue - - if control.widthMode == WidthMode_Expand: - if container.layout == Layout_Horizontal: - minInnerWidth.inc(control.minWidth) - expandWidthCount.inc - - elif control.widthMode in {WidthMode_Auto , WidthMode_Static}: - if container.layout == Layout_Horizontal: - if control.wantedWidth == -1: - minInnerWidth.inc(control.minWidth) - expandWidthCount.inc - else: - minInnerWidth.inc(control.wantedWidth) - else: - if control.wantedWidth == -1: - expandWidthCount.inc - else: - minInnerWidth = max(minInnerWidth, control.wantedWidth) - - if control.heightMode == HeightMode_Expand: - if container.layout == Layout_Vertical: - minInnerHeight.inc(control.minHeight) - expandHeightCount.inc - - elif control.heightMode in {HeightMode_Auto, HeightMode_Static}: - if container.layout == Layout_Vertical: - if control.wantedHeight == -1: - minInnerHeight.inc(control.minHeight) - expandHeightCount.inc - else: - minInnerHeight.inc(control.wantedHeight) - else: - if control.wantedHeight == -1: - expandHeightCount.inc - else: - minInnerHeight = max(minInnerHeight, control.wantedHeight) - - # Add padding: - minInnerWidth.inc(container.padding * 2) - minInnerHeight.inc(container.padding * 2) - - # Add spacing: - if container.childControls.len > 1: - if container.layout == Layout_Horizontal: - minInnerWidth.inc(container.spacing * (container.childControls.len - 1)) - if container.layout == Layout_Vertical: - minInnerHeight.inc(container.spacing * (container.childControls.len - 1)) - - container.updateInnerSize(minInnerWidth, minInnerHeight) - - let innerWidth = max(minInnerWidth, clientWidth) - let innerHeight = max(minInnerHeight, clientHeight) - - # Calculate dynamic size: - var dynamicWidth = clientWidth - minInnerWidth - var dynamicHeight = clientHeight - minInnerHeight - dynamicWidth = max(dynamicWidth, 0) - dynamicHeight = max(dynamicHeight, 0) - - # Move and resize controls: - var x = container.padding - var y = container.padding - - if (container.xAlign == XAlign_Center or (container.xAlign == XAlign_Spread and container.childControls.len == 1)) and (container.layout == Layout_Vertical or expandWidthCount == 0): - x.inc(dynamicWidth div 2) - if (container.yAlign == YAlign_Center or (container.yAlign == YAlign_Spread and container.childControls.len == 1)) and (container.layout == Layout_Horizontal or expandHeightCount == 0): - y.inc(dynamicHeight div 2) - - for control in container.fChildControls: - if not control.visible: - continue - # echo " child: " & control.tag - - # Size: - var width = control.width - var height = control.height - # echo "size old: " & $width & ", " $height - if control.widthMode == WidthMode_Expand or control.wantedWidth == -1: - if container.layout == Layout_Horizontal: - if expandWidthCount > 0: - width = control.minWidth + dynamicWidth div expandWidthCount - else: - width = control.minWidth + dynamicWidth - else: - width = clientWidth - container.padding * 2 - elif control.widthMode == WidthMode_Auto: - width = control.wantedWidth - elif control.widthMode == WidthMode_Fill: - width = clientWidth - container.padding * 2 - - if control.minWidth > width: - width = control.minWidth - if control.maxWidth > 0 and control.maxWidth < width: - width = control.maxWidth - - if control.heightMode == HeightMode_Expand or control.wantedHeight == -1: - if container.layout == Layout_Vertical: - if expandHeightCount > 0: - height = control.minHeight + dynamicHeight div expandHeightCount - else: - height = control.minHeight + dynamicHeight - else: - height = clientHeight - container.padding * 2 - elif control.heightMode == HeightMode_Auto: - height = control.wantedHeight - elif control.heightMode == HeightMode_Fill: - height = clientHeight - container.padding * 2 - - if control.minHeight > height: - height = control.minHeight - if control.maxHeight > 0 and control.maxHeight < height: - height = control.maxHeight - - if control.width != width or control.height != height: - # echo " child: " & control.tag - # echo " new size: " & $width & ", " & $height - control.setSize(width, height) - - # Position: - if container.layout == Layout_Vertical or container.childControls.len == 1: - if container.xAlign == XAlign_Center: - x = (innerWidth - width) div 2 - elif container.xAlign == XAlign_Right: - x = innerWidth - width - container.padding - if container.layout == Layout_Horizontal or container.childControls.len == 1: - if container.yAlign == YAlign_Center: - y = (innerHeight - height) div 2 - elif container.yAlign == YAlign_Bottom: - y = innerHeight - height - container.padding - - if control.x != x or control.y != y: - # echo " new pos: " & $x & ", " $y - control.setPosition(x, y) - - # Calculate next position: - case container.layout - of Layout_Horizontal: - x.inc(width) - x.inc(container.spacing) - if container.xAlign == XAlign_Spread and expandWidthCount == 0 and container.childControls.len > 1: - x.inc(dynamicWidth div (container.childControls.len - 1)) - of Layout_Vertical: - y.inc(height) - y.inc(container.spacing) - if container.yAlign == YAlign_Spread and expandHeightCount == 0 and container.childControls.len > 1: - y.inc(dynamicHeight div (container.childControls.len - 1)) - - -# ---------------------------------------------------------------------------------------- -# Frame -# ---------------------------------------------------------------------------------------- - -proc newFrame(text = ""): Frame = - result = new NativeFrame - result.NativeFrame.init() - result.text = text - -proc init(frame: Frame) = - frame.ControlImpl.init() - frame.fText = "" - -method text(frame: Frame): string = frame.fText - -method `text=`(frame: Frame, text: string) = - frame.fText = text - frame.tag = text - # should be extended by NativeFrame - -method getPadding(frame: Frame): Spacing = - result.left = 4 - result.right = 4 - result.top = 4 - result.bottom = 4 - # should be extended by NativeFrame - -method `onDraw=`(container: NativeFrame, callback: DrawProc) = raiseError("NativeFrame does not allow onDraw.") - - -# ---------------------------------------------------------------------------------------- -# Button -# ---------------------------------------------------------------------------------------- - -proc newButton(text = ""): Button = - result = new NativeButton - result.NativeButton.init() - result.text = text - -proc init(button: Button) = - button.ControlImpl.init() - button.fText = "" - button.fOnClick = nil - button.fWidthMode = WidthMode_Auto - button.fHeightMode = HeightMode_Auto - button.minWidth = 15 - button.minHeight = 15 - -method text(button: Button): string = button.fText - -method `text=`(button: Button, text: string) = - button.fText = text - button.tag = text - button.triggerRelayoutIfModeIsAuto() - # should be extended by NativeButton - -method naturalWidth(button: Button): int = button.getTextWidth(button.text) + 20 - -method naturalHeight(button: Button): int = button.getTextLineHeight() * button.text.countLines + 12 - -method `onDraw=`(container: NativeButton, callback: DrawProc) = raiseError("NativeButton does not allow onDraw.") - - -# ---------------------------------------------------------------------------------------- -# Label -# ---------------------------------------------------------------------------------------- - -proc newLabel(text = ""): Label = - result = new NativeLabel - result.NativeLabel.init() - result.text = text - -proc init(label: Label) = - label.ControlImpl.init() - label.fText = "" - label.fWidthMode = WidthMode_Auto - label.fHeightMode = HeightMode_Auto - label.minWidth = 10 - label.minHeight = 10 - -method text(label: Label): string = label.fText - -method `text=`(label: Label, text: string) = - label.fText = text - label.tag = text - label.triggerRelayoutIfModeIsAuto() - -method naturalWidth(label: Label): int = label.getTextWidth(label.text) - -method naturalHeight(label: Label): int = label.getTextLineHeight() * label.text.countLines - -method `onDraw=`(container: NativeLabel, callback: DrawProc) = raiseError("NativeLabel does not allow onDraw.") - - -# ---------------------------------------------------------------------------------------- -# TextBox -# ---------------------------------------------------------------------------------------- - -proc newTextBox(text = ""): TextBox = - result = new NativeTextBox - result.NativeTextBox.init() - result.text = text - -proc init(textBox: TextBox) = - textBox.ControlImpl.init() - textBox.fWidthMode = WidthMode_Expand - textBox.fHeightMode = HeightMode_Auto - textBox.minWidth = 20 - textBox.minHeight = 20 - -method naturalHeight(textBox: TextBox): int = textBox.getTextLineHeight() - -method text(textBox: TextBox): string = discard - # has to be implemented by NativeTextBox - -method `text=`(textBox: TextBox, text: string) = discard - # has to be implemented by NativeTextBox - -method `onDraw=`(container: NativeTextBox, callback: DrawProc) = raiseError("NativeTextBox does not allow onDraw.") - - -# ---------------------------------------------------------------------------------------- -# TextArea -# ---------------------------------------------------------------------------------------- - -proc newTextArea(text = ""): TextArea = - result = new NativeTextArea - result.NativeTextArea.init() - result.text = text - -proc init(textArea: TextArea) = - textArea.ControlImpl.init() - textArea.fWidthMode = WidthMode_Expand - textArea.fHeightMode = HeightMode_Expand - textArea.minWidth = 20 - textArea.minHeight = 20 - textArea.wrap = true - -method text(textArea: TextArea): string = discard - # has to be implemented by NativeTextBox - -method `text=`(textArea: TextArea, text: string) = discard - # has to be implemented by NativeTextBox - -method addText(textArea: TextArea, text: string) = textArea.text = textArea.text & text - -method addLine(textArea: TextArea, text = "") = textArea.addtext(text & "\n") - -method scrollToBottom(textArea: TextArea) = discard - # has to be implemented by NativeTextBox - -method `onDraw=`(container: NativeTextArea, callback: DrawProc) = raiseError("NativeTextArea does not allow onDraw.") - -method wrap(textArea: TextArea): bool = textArea.fWrap - -method `wrap=`(textArea: TextArea, wrap: bool) = - textArea.fWrap = wrap - # should be extended by NativeTextArea - - -# ---------------------------------------------------------------------------------------- -# Platform-specific implementation -# ---------------------------------------------------------------------------------------- - -include nigui_platform_impl diff --git a/src/gtk3/gtk3.nim b/src/gtk3/gtk3.nim deleted file mode 100755 index f2c7eba..0000000 --- a/src/gtk3/gtk3.nim +++ /dev/null @@ -1,430 +0,0 @@ -# NiGui - minimal GTK+ 3 binding - -{.deadCodeElim: on.} - -when defined(windows): - const libgtk3Path* = "libgtk-3-0.dll" -elif defined(gtk_quartz): - const libgtk3Path* = "libgtk-3.0.dylib" -elif defined(macosx): - const libgtk3Path* = "libgtk-x11-3.0.dylib" -else: - const libgtk3Path* = "libgtk-3.so(|.0)" - -{.pragma: libgtk3, cdecl, dynlib: libgtk3Path.} - -# ---------------------------------------------------------------------------------------- -# Types -# ---------------------------------------------------------------------------------------- - -type - GError* {.byCopy.} = object - domain*: int32 - code*: cint - message*: cstring - - GdkRectangle* {.byCopy.} = object - x*, y*: cint - width*, height*: cint - - GtkBorder* {.byCopy.} = object - left*: int16 - right*: int16 - top*: int16 - bottom*: int16 - - GdkRGBA* {.byCopy.} = object - red*: cdouble - green*: cdouble - blue*: cdouble - alpha*: cdouble - - GList* = ptr object - data*: pointer - next*: GList - prev*: GList - - GtkTargetEntry* {.byCopy.} = object - target*: cstring - flags*: cint - info*: cint - - GdkEventButton* {.byCopy.} = object - event_type*: cint - window*: pointer - send_event*: int8 - time*: cint - x*, y*: cdouble - axes*: ptr cdouble - state*: cint - button*: cint - device*: pointer - x_root*, y_root*: cdouble - - GdkEventKey* {.byCopy.} = object - event_type*: cint - window*: pointer - send_event*: int8 - time*: cint - state*: cint - keyval*: cint - length*: cint - `string`*: cstring - hardware_keycode*: int16 - group*: int8 - is_modifier*: int8 - - GtkTextIter* {.byCopy.} = object - dummy1: pointer - dummy2: pointer - dummy3: cint - dummy4: cint - dummy5: cint - dummy6: cint - dummy7: cint - dummy8: cint - dummy9: pointer - dummy10: pointer - dummy11: cint - dummy12: cint - dummy13: cint - dummy14: pointer - -# ---------------------------------------------------------------------------------------- -# Constants -# ---------------------------------------------------------------------------------------- - -const - # GtkWindowType: - GTK_WINDOW_TOPLEVEL* = 0 - GTK_WINDOW_POPUP* = 1 - - # GtkDestDefaults: - # [..] - GTK_DEST_DEFAULT_ALL* = 7 - - # GdkDragAction: - GDK_ACTION_DEFAULT* = 1 - GDK_ACTION_COPY* = 2 - GDK_ACTION_MOVE* = 4 - GDK_ACTION_LINK* = 8 - GDK_ACTION_PRIVATE* = 16 - GDK_ACTION_ASK* = 32 - - # GtkOrientation: - GTK_ORIENTATION_HORIZONTAL* = 0 - GTK_ORIENTATION_VERTICAL* = 1 - - # GtkWrapMode: - GTK_WRAP_NONE* = 0 - GTK_WRAP_CHAR* = 1 - GTK_WRAP_WORD* = 2 - GTK_WRAP_WORD_CHAR* = 3 - - # GtkPolicyType: - GTK_POLICY_ALWAYS* = 0 - GTK_POLICY_AUTOMATIC* = 1 - GTK_POLICY_NEVER* = 2 - GTK_POLICY_EXTERNAL* = 3 - - # PangoEllipsizeMode: - PANGO_ELLIPSIZE_NONE* = 0 - PANGO_ELLIPSIZE_START* = 1 - PANGO_ELLIPSIZE_MIDDLE* = 2 - PANGO_ELLIPSIZE_END* = 3 - - # GtkButtonBoxStyle: - GTK_BUTTONBOX_SPREAD* = 0 - GTK_BUTTONBOX_EDGE* = 1 - GTK_BUTTONBOX_START* = 2 - GTK_BUTTONBOX_END* = 3 - GTK_BUTTONBOX_CENTER* = 4 - GTK_BUTTONBOX_EXPAND* = 5 - - # GtkJustification: - GTK_JUSTIFY_LEFT* = 0 - GTK_JUSTIFY_RIGHT* = 1 - GTK_JUSTIFY_CENTER* = 2 - GTK_JUSTIFY_FILL* = 3 - - # GtkStateFlags: - GTK_STATE_FLAG_NORMAL* = 0 - # [..] - - # GdkEventMask: - GDK_BUTTON_PRESS_MASK* = 256 - GDK_BUTTON_RELEASE_MASK* = 512 - GDK_KEY_PRESS_MASK* = 1024 - # [..] - - # cairo_format_t: - CAIRO_FORMAT_ARGB32* = 0 - # [..] - - # GtkFileChooserAction: - GTK_FILE_CHOOSER_ACTION_OPEN* = 0 - GTK_FILE_CHOOSER_ACTION_SAVE* = 1 - GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER* = 2 - GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER* = 3 - - # GtkResponseType: - GTK_RESPONSE_NONE* = -1 - GTK_RESPONSE_REJECT* = -2 - GTK_RESPONSE_ACCEPT* = -3 - GTK_RESPONSE_DELETE_EVENT* = -4 - GTK_RESPONSE_OK* = -5 - GTK_RESPONSE_CANCEL* = -6 - GTK_RESPONSE_CLOSE* = -7 - GTK_RESPONSE_YES* = -8 - GTK_RESPONSE_NO* = -9 - GTK_RESPONSE_APPLY* = -10 - GTK_RESPONSE_HELP* = -11 - -# ---------------------------------------------------------------------------------------- -# General Gtk Procs -# ---------------------------------------------------------------------------------------- - -proc g_slist_length*(list: pointer): int {.importc: "g_slist_length", libgtk3.} -proc g_slist_nth_data_string*(list: pointer, n: int): cstring {.importc: "g_slist_nth_data", libgtk3.} - -proc gtk_init*(argc, argv: pointer) {.importc: "gtk_init", libgtk3.} - -proc gtk_main*() {.importc: "gtk_main", libgtk3.} -proc gtk_main_quit*() {.importc: "gtk_main_quit", libgtk3.} -proc gtk_events_pending*(): cint {.importc: "gtk_events_pending", libgtk3.} -proc gtk_main_iteration*(): cint {.importc: "gtk_main_iteration", libgtk3.} -proc g_timeout_add*(interval: cint, function, data: pointer): cint {.importc: "g_timeout_add", libgtk3.} -proc g_source_remove*(tag: cint): bool {.importc: "g_source_remove", libgtk3.} -proc g_signal_connect_data*(instance: pointer, detailed_signal: cstring, c_handler: pointer, data, destroy_data, connect_flags: pointer = nil): pointer {.importc: "g_signal_connect_data", libgtk3.} - -proc gtk_window_new*(`type`: cint): pointer {.importc: "gtk_window_new", libgtk3.} -proc gtk_window_set_title*(window: pointer, title: cstring) {.importc: "gtk_window_set_title", libgtk3.} -# proc gtk_window_get_title*(window: pointer): cstring {.importc: "gtk_window_get_title", libgtk3.} -proc gtk_window_set_transient_for*(window, parent: pointer) {.importc: "gtk_window_set_transient_for", libgtk3.} -proc gtk_window_set_modal*(window: pointer, modal: cint) {.importc: "gtk_window_set_modal", libgtk3.} -# proc gtk_window_set_default_size*(window: pointer, width, height: cint) {.importc: "gtk_window_set_default_size", libgtk3.} -proc gtk_window_resize*(window: pointer, width, height: cint) {.importc: "gtk_window_resize", libgtk3.} -proc gtk_window_resize_to_geometry*(window: pointer, width, height: cint) {.importc: "gtk_window_resize_to_geometry", libgtk3.} -proc gtk_window_get_size*(window: pointer, width, height: var cint) {.importc: "gtk_window_get_size", libgtk3.} -proc gtk_window_get_position*(window: pointer, x, y: var cint) {.importc: "gtk_window_get_position", libgtk3.} -proc gtk_window_move*(window: pointer, x, y: cint) {.importc: "gtk_window_move", libgtk3.} -proc gtk_window_set_icon_from_file*(window: pointer, filename: cstring, err: pointer) {.importc: "gtk_window_set_icon_from_file", libgtk3.} - -proc gdk_window_begin_paint_rect*(window: pointer, rectangle: var GdkRectangle) {.importc: "gdk_window_begin_paint_rect", libgtk3.} -proc gdk_window_begin_paint_region*(window: pointer, region: pointer) {.importc: "gdk_window_begin_paint_region", libgtk3.} -proc gdk_window_end_paint*(window: pointer) {.importc: "gdk_window_end_paint", libgtk3.} -proc gdk_window_get_clip_region*(window: pointer): pointer {.importc: "gdk_window_get_clip_region", libgtk3.} - -proc gtk_widget_destroy*(widget: pointer) {.importc: "gtk_widget_destroy", libgtk3.} -proc gtk_widget_show*(widget: pointer) {.importc: "gtk_widget_show", libgtk3.} -proc gtk_widget_hide*(widget: pointer) {.importc: "gtk_widget_hide", libgtk3.} -proc gtk_widget_set_size_request*(widget: pointer, width, height: cint) {.importc: "gtk_widget_set_size_request", libgtk3.} -proc gtk_widget_size_allocate*(widget: pointer, allocation: var GdkRectangle) {.importc: "gtk_widget_size_allocate", libgtk3.} -proc gtk_widget_get_size_request*(widget: pointer, width, height: var cint) {.importc: "gtk_widget_get_size_request", libgtk3.} -proc gtk_widget_get_allocation*(widget: pointer, allocation: var GdkRectangle) {.importc: "gtk_widget_get_allocation", libgtk3.} -# proc gtk_widget_set_allocation*(widget: pointer, allocation: var GdkRectangle) {.importc: "gtk_widget_set_allocation", libgtk3.} -# proc gtk_widget_set_hexpand*(widget: pointer, expand: cint) {.importc: "gtk_widget_set_hexpand", libgtk3.} -proc gtk_widget_queue_draw*(widget: pointer) {.importc: "gtk_widget_queue_draw", libgtk3.} -proc gtk_widget_set_margin_top*(widget: pointer, margin: cint) {.importc: "gtk_widget_set_margin_top", libgtk3.} -proc gtk_widget_add_events*(widget: pointer, events: cint) {.importc: "gtk_widget_add_events", libgtk3.} -proc gtk_widget_set_can_focus*(widget: pointer, can_focus: cint) {.importc: "gtk_widget_set_can_focus", libgtk3.} -proc gtk_widget_modify_font*(widget: pointer, font_desc: pointer) {.importc: "gtk_widget_modify_font", libgtk3.} -proc gtk_widget_override_color*(widget: pointer, state: cint, color: var GdkRGBA) {.importc: "gtk_widget_override_color", libgtk3.} -proc gtk_widget_override_background_color*(widget: pointer, state: cint, color: var GdkRGBA) {.importc: "gtk_widget_override_background_color", libgtk3.} -proc gtk_widget_get_path*(widget: pointer): pointer {.importc: "gtk_widget_get_path", libgtk3.} -proc gtk_widget_style_get*(widget: pointer, first_property_name: cstring, value: pointer, passNil: pointer) {.importc: "gtk_widget_style_get", libgtk3.} -proc gtk_widget_get_style_context*(widget: pointer): pointer {.importc: "gtk_widget_get_style_context", libgtk3.} -proc gtk_widget_grab_focus*(widget: pointer) {.importc: "gtk_widget_grab_focus", libgtk3.} -proc gtk_widget_is_focus*(widget: pointer): bool {.importc: "gtk_widget_is_focus", libgtk3.} -proc gtk_widget_realize*(widget: pointer) {.importc: "gtk_widget_realize", libgtk3.} -proc gtk_widget_draw*(widget, cr: pointer) {.importc: "gtk_widget_draw", libgtk3.} - -proc gtk_container_add*(container, widget: pointer) {.importc: "gtk_container_add", libgtk3.} -proc gtk_container_remove*(container, widget: pointer) {.importc: "gtk_container_remove", libgtk3.} -# proc gtk_container_foreach*(container, callback, callback_data: pointer) {.importc: "gtk_container_foreach", libgtk3.} -proc gtk_container_get_children*(container: pointer): GList {.importc: "gtk_container_get_children", libgtk3.} -proc gtk_container_set_border_width*(container: pointer, width: cint) {.importc: "gtk_container_set_border_width", libgtk3.} - -proc gtk_fixed_new*(): pointer {.importc: "gtk_fixed_new", libgtk3.} -proc gtk_fixed_move*(fixed, widget: pointer, x, y: cint) {.importc: "gtk_fixed_move", libgtk3.} - -proc gtk_layout_new*(hadjustment, vadjustment: pointer): pointer {.importc: "gtk_layout_new", libgtk3.} -# proc gtk_layout_put*(layout, child_widget: pointer, x, y: cint) {.importc: "gtk_layout_put", libgtk3.} -proc gtk_layout_move*(layout, child_widget: pointer, x, y: cint) {.importc: "gtk_layout_move", libgtk3.} -# proc gtk_layout_set_size*(layout: pointer, width, height: cint) {.importc: "gtk_layout_set_size", libgtk3.} -# proc gtk_layout_get_hadjustment*(layout: pointer): pointer {.importc: "gtk_layout_get_hadjustment", libgtk3.} -# proc gtk_layout_get_vadjustment*(layout: pointer): pointer {.importc: "gtk_layout_get_vadjustment", libgtk3.} - -# proc gtk_scrollbar_new*(orientation: GtkOrientation, adjustment: pointer): pointer {.importc: "gtk_scrollbar_new", libgtk3.} - -proc gtk_dialog_new*(): pointer {.importc: "gtk_dialog_new", libgtk3.} -proc gtk_dialog_run*(dialog: pointer): cint {.importc: "gtk_dialog_run", libgtk3.} -proc gtk_dialog_add_button*(dialog: pointer, button_text: cstring, response_id: cint): pointer {.importc: "gtk_dialog_add_button", libgtk3.} -proc gtk_dialog_get_content_area*(dialog: pointer): pointer {.importc: "gtk_dialog_get_content_area", libgtk3.} -proc gtk_dialog_get_action_area*(dialog: pointer): pointer {.importc: "gtk_dialog_get_action_area", libgtk3.} - -proc gtk_file_chooser_dialog_new*(title: string, parent: pointer, action: int, text1: cstring, response1: int, text2: cstring, response2: int, ending: pointer): pointer {.importc: "gtk_file_chooser_dialog_new", libgtk3.} -proc gtk_file_chooser_set_current_name*(chooser: pointer, name: cstring): bool {.importc: "gtk_file_chooser_set_current_name", libgtk3.} -proc gtk_file_chooser_get_filename*(chooser: pointer): cstring {.importc: "gtk_file_chooser_get_filename", libgtk3.} -proc gtk_file_chooser_get_filenames*(chooser: pointer): pointer {.importc: "gtk_file_chooser_get_filenames", libgtk3.} -proc gtk_file_chooser_set_select_multiple*(chooser: pointer, select_multiple: bool) {.importc: "gtk_file_chooser_set_select_multiple", libgtk3.} -proc gtk_file_chooser_set_current_folder*(chooser: pointer, filename: cstring): bool {.importc: "gtk_file_chooser_set_current_folder", libgtk3.} - -proc gtk_button_box_set_layout*(widget: pointer, layout_style: cint) {.importc: "gtk_button_box_set_layout", libgtk3.} - -# proc gtk_message_dialog_new*(parent: pointer, flags: GtkDialogFlags, `type`: GtkMessageType, buttons: GtkButtonsType, message_format: cstring): pointer {.importc: "gtk_message_dialog_new", libgtk3.} - -proc gtk_label_new*(str: cstring): pointer {.importc: "gtk_label_new", libgtk3.} -proc gtk_label_set_text*(label: pointer, str: cstring) {.importc: "gtk_label_set_text", libgtk3.} -# proc gtk_label_get_text*(label: pointer): cstring {.importc: "gtk_label_get_text", libgtk3.} -proc gtk_label_set_ellipsize*(label: pointer, mode: cint) {.importc: "gtk_label_set_ellipsize", libgtk3.} -# proc gtk_label_set_justify*(label: pointer, jtype: cint) {.importc: "gtk_label_set_justify", libgtk3.} -proc gtk_label_set_xalign*(label: pointer, xalign: cfloat) {.importc: "gtk_label_set_xalign", libgtk3.} -proc gtk_label_set_yalign*(label: pointer, yalign: cfloat) {.importc: "gtk_label_set_yalign", libgtk3.} - -# proc gtk_box_new*(orientation: GtkOrientation, spacing: cint): pointer {.importc: "gtk_box_new", libgtk3.} -proc gtk_box_pack_start*(box, child: pointer, expand, fill: cint, padding: cint) {.importc: "gtk_box_pack_start", libgtk3.} - -proc gtk_button_new*(): pointer {.importc: "gtk_button_new", libgtk3.} -# proc gtk_button_new_with_label*(label: cstring): pointer {.importc: "gtk_button_new_with_label", libgtk3.} -# proc gtk_button_get_label*(button: pointer): cstring {.importc: "gtk_button_get_label", libgtk3.} -proc gtk_button_set_label*(button: pointer, label: cstring) {.importc: "gtk_button_set_label", libgtk3.} - -proc gtk_entry_new*(): pointer {.importc: "gtk_entry_new", libgtk3.} -proc gtk_entry_set_text*(entry: pointer, text: cstring) {.importc: "gtk_entry_set_text", libgtk3.} -proc gtk_entry_get_text*(entry: pointer): cstring {.importc: "gtk_entry_get_text", libgtk3.} -proc gtk_entry_set_width_chars*(entry: pointer, n_chars: cint) {.importc: "gtk_entry_set_width_chars", libgtk3.} - -proc gtk_text_view_new*(): pointer {.importc: "gtk_text_view_new", libgtk3.} -proc gtk_text_view_set_buffer*(text_view, buffer: pointer) {.importc: "gtk_text_view_set_buffer", libgtk3.} -proc gtk_text_view_get_buffer*(text_view: pointer): pointer {.importc: "gtk_text_view_get_buffer", libgtk3.} -proc gtk_text_view_set_wrap_mode*(text_view: pointer, wrap_mode: cint) {.importc: "gtk_text_view_set_wrap_mode", libgtk3.} -proc gtk_text_view_set_left_margin*(text_view: pointer, margin: cint) {.importc: "gtk_text_view_set_left_margin", libgtk3.} -proc gtk_text_view_set_right_margin*(text_view: pointer, margin: cint) {.importc: "gtk_text_view_set_right_margin", libgtk3.} -proc gtk_text_view_set_top_margin*(text_view: pointer, margin: cint) {.importc: "gtk_text_view_set_top_margin", libgtk3.} -proc gtk_text_view_set_bottom_margin*(text_view: pointer, margin: cint) {.importc: "gtk_text_view_set_bottom_margin", libgtk3.} -proc gtk_text_view_scroll_to_iter*(text_view: pointer, iter: var GtkTextIter, within_margin: cfloat, use_align: bool, xalign, yalign: cfloat) {.importc: "gtk_text_view_scroll_to_iter", libgtk3.} -# proc gtk_text_view_scroll_to_mark*(text_view, mark: pointer, within_margin: cfloat, use_align: bool, xalign, yalign: cfloat) {.importc: "gtk_text_view_scroll_to_mark", libgtk3.} -# proc gtk_text_view_place_cursor_onscreen*(text_view: pointer): bool {.importc: "gtk_text_view_place_cursor_onscreen", libgtk3.} - -# proc gtk_text_mark_new*(name: cstring, left_gravity: bool): pointer {.importc: "gtk_text_mark_new", libgtk3.} - -# proc gtk_text_buffer_new*(table: pointer): pointer {.importc: "gtk_text_buffer_new", libgtk3.} -proc gtk_text_buffer_set_text*(text_buffer: pointer, text: cstring, len: cint) {.importc: "gtk_text_buffer_set_text", libgtk3.} -proc gtk_text_buffer_get_text*(text_buffer: pointer, start, `end`: var GtkTextIter, include_hidden_chars: cint): cstring {.importc: "gtk_text_buffer_get_text", libgtk3.} -proc gtk_text_buffer_get_start_iter*(text_buffer: pointer, iter: var GtkTextIter) {.importc: "gtk_text_buffer_get_start_iter", libgtk3.} -proc gtk_text_buffer_get_end_iter*(text_buffer: pointer, iter: var GtkTextIter) {.importc: "gtk_text_buffer_get_end_iter", libgtk3.} -# proc gtk_text_buffer_add_mark*(buffer, mark: pointer, where: var GtkTextIter) {.importc: "gtk_text_buffer_add_mark", libgtk3.} -# proc gtk_text_buffer_get_insert*(buffer: pointer): pointer {.importc: "gtk_text_buffer_get_insert", libgtk3.} -# proc gtk_text_buffer_get_iter_at_line*(buffer: pointer, iter: var GtkTextIter, line_number: cint) {.importc: "gtk_text_buffer_get_iter_at_line", libgtk3.} -proc gtk_text_buffer_insert*(buffer: pointer, iter: var GtkTextIter, text: cstring, len: cint) {.importc: "gtk_text_buffer_insert", libgtk3.} - -proc gtk_scrolled_window_new*(hadjustment, vadjustment: pointer): pointer {.importc: "gtk_scrolled_window_new", libgtk3.} -proc gtk_scrolled_window_set_policy*(scrolled_window: pointer, hscrollbar_policy, vscrollbar_policy: cint) {.importc: "gtk_scrolled_window_set_policy", libgtk3.} -proc gtk_scrolled_window_get_hscrollbar*(scrolled_window: pointer): pointer {.importc: "gtk_scrolled_window_get_hscrollbar", libgtk3.} -proc gtk_scrolled_window_get_vscrollbar*(scrolled_window: pointer): pointer {.importc: "gtk_scrolled_window_get_vscrollbar", libgtk3.} -proc gtk_scrolled_window_get_hadjustment*(scrolled_window: pointer): pointer {.importc: "gtk_scrolled_window_get_hadjustment", libgtk3.} -proc gtk_scrolled_window_get_vadjustment*(scrolled_window: pointer): pointer {.importc: "gtk_scrolled_window_get_vadjustment", libgtk3.} -# proc gtk_scrolled_window_get_max_content_width*(scrolled_window: pointer): cint {.importc: "gtk_scrolled_window_get_max_content_width", libgtk3.} -# proc gtk_scrolled_window_get_min_content_width*(scrolled_window: pointer): cint {.importc: "gtk_scrolled_window_get_min_content_width", libgtk3.} -# proc gtk_scrolled_window_set_overlay_scrolling*(scrolled_window: pointer, overlay_scrolling: bool) {.importc: "gtk_scrolled_window_set_overlay_scrolling", libgtk3.} - -proc gtk_frame_new*(label: cstring): pointer {.importc: "gtk_frame_new", libgtk3.} -proc gtk_frame_set_label*(frame: pointer, label: cstring) {.importc: "gtk_frame_set_label", libgtk3.} -proc gtk_frame_get_label_widget*(frame: pointer): pointer {.importc: "gtk_frame_get_label_widget", libgtk3.} - -proc gtk_drawing_area_new*(): pointer {.importc: "gtk_drawing_area_new", libgtk3.} - -proc gtk_adjustment_get_value*(adjustment: pointer): cdouble {.importc: "gtk_adjustment_get_value", libgtk3.} -proc gtk_adjustment_set_value*(adjustment: pointer, value: cdouble) {.importc: "gtk_adjustment_set_value", libgtk3.} -proc gtk_adjustment_set_upper*(adjustment: pointer, upper: cdouble) {.importc: "gtk_adjustment_set_upper", libgtk3.} -proc gtk_adjustment_get_upper*(adjustment: pointer): cdouble {.importc: "gtk_adjustment_get_upper", libgtk3.} -proc gtk_adjustment_set_page_size*(adjustment: pointer, page_size: cdouble) {.importc: "gtk_adjustment_set_page_size", libgtk3.} -proc gtk_adjustment_get_page_size*(adjustment: pointer): cdouble {.importc: "gtk_adjustment_get_page_size", libgtk3.} - -proc gtk_drag_dest_set*(widget: pointer, flags: cint, targets: pointer, n_targets: cint, actions: cint) {.importc: "gtk_drag_dest_set", libgtk3.} - -proc gdk_keyval_to_unicode*(keyval: cint): cint {.importc: "gdk_keyval_to_unicode", libgtk3.} - -proc gdk_screen_get_default*(): pointer {.importc: "gdk_screen_get_default", libgtk3.} -proc gdk_screen_get_primary_monitor*(screen: pointer): cint {.importc: "gdk_screen_get_primary_monitor", libgtk3.} -# proc gdk_screen_get_width*(screen: pointer): cint {.importc: "gdk_screen_get_width", libgtk3.} -# proc gdk_screen_get_height*(screen: pointer): cint {.importc: "gdk_screen_get_height", libgtk3.} -proc gdk_screen_get_monitor_workarea*(screen: pointer, monitor_num: cint, dest: var GdkRectangle) {.importc: "gdk_screen_get_monitor_workarea", libgtk3.} - -proc gtk_style_context_get_padding*(context: pointer, state: cint, padding: var GtkBorder) {.importc: "gtk_style_context_get_padding", libgtk3.} -proc gtk_style_context_get_background_color*(context: pointer, state: cint, color: var GdkRGBA) {.importc: "gtk_style_context_get_background_color", libgtk3.} -proc gtk_style_context_get_color*(context: pointer, state: cint, color: var GdkRGBA) {.importc: "gtk_style_context_get_color", libgtk3.} -# proc gtk_style_context_get_font*(context: pointer, state: cint): pointer {.importc: "gtk_style_context_get_font", libgtk3.} - -proc gtk_border_new*(): pointer {.importc: "gtk_border_new", libgtk3.} - -# proc gdk_threads_init*() {.importc: "gdk_threads_init", libgtk3.} -# proc gdk_threads_add_idle*(function, data: pointer): cint {.importc: "gdk_threads_add_idle", libgtk3.} - -proc gtk_scrollbar_new*(orientation: cint, adjustment: pointer): pointer {.importc: "gtk_scrollbar_new", libgtk3.} - -proc gtk_adjustment_new*(value, lower, upper, step_increment, page_increment, page_size: cdouble): pointer {.importc: "gtk_adjustment_new", libgtk3.} - -# proc gtk_selection_data_get_length*(selection_data: pointer): cint {.importc: "gtk_selection_data_get_length", libgtk3.} -# proc gtk_selection_data_get_text*(selection_data: pointer): cstring {.importc: "gtk_selection_data_get_text", libgtk3.} - -proc gtk_selection_data_get_uris*(selection_data: pointer): ptr cstring {.importc: "gtk_selection_data_get_uris", libgtk3.} -proc g_filename_from_uri*(uri: pointer): cstring {.importc: "g_filename_from_uri", libgtk3.} - - -# ---------------------------------------------------------------------------------------- -# Drawing Related Procs -# ---------------------------------------------------------------------------------------- - -proc gtk_widget_create_pango_layout*(widget: pointer, text: cstring): pointer {.importc: "gtk_widget_create_pango_layout", libgtk3.} -proc gdk_cairo_set_source_rgba*(cr: pointer, rgba: var GdkRGBA) {.importc: "gdk_cairo_set_source_rgba", libgtk3.} -proc gdk_cairo_surface_create_from_pixbuf*(pixbuf: pointer, scale: cint, for_window: pointer): pointer {.importc: "gdk_cairo_surface_create_from_pixbuf", libgtk3.} -proc gdk_pixbuf_new_from_file*(filename: cstring, error: pointer): pointer {.importc: "gdk_pixbuf_new_from_file", libgtk3.} -proc gdk_pixbuf_save*(pixbuf: pointer, filename, `type`: cstring, error: pointer, param5, param6, param7: cstring): bool {.importc: "gdk_pixbuf_save", libgtk3.} -proc gdk_pixbuf_get_from_surface*(surface: pointer, src_x, src_y, width, height: cint): pointer {.importc: "gdk_pixbuf_get_from_surface", libgtk3.} -# proc gdk_pixmap_create_from_data*(drawable, data: pointer, width, height, depth: cint, fg, bg: var GdkRGBA): pointer {.importc: "gdk_pixmap_create_from_data", libgtk3.} - -proc cairo_image_surface_create*(format: cint, width, height: cint): pointer {.importc: "cairo_image_surface_create", libgtk3.} -# proc cairo_image_surface_create_for_data*(data: pointer, format: cairo_format_t, width, height, stride: cint): pointer {.importc: "cairo_image_surface_create_for_data", libgtk3.} -proc cairo_image_surface_get_width*(surface: pointer): cint {.importc: "cairo_image_surface_get_width", libgtk3.} -proc cairo_image_surface_get_height*(surface: pointer): cint {.importc: "cairo_image_surface_get_height", libgtk3.} -proc cairo_image_surface_get_stride*(surface: pointer): cint {.importc: "cairo_image_surface_get_stride", libgtk3.} -proc cairo_image_surface_get_data*(surface: pointer): cstring {.importc: "cairo_image_surface_get_data", libgtk3.} -proc cairo_surface_flush*(surface: pointer) {.importc: "cairo_surface_flush", libgtk3.} -proc cairo_surface_mark_dirty*(surface: pointer) {.importc: "cairo_surface_mark_dirty", libgtk3.} -proc cairo_surface_destroy*(surface: pointer) {.importc: "cairo_surface_destroy", libgtk3.} - -# proc cairo_format_stride_for_width*(format: cairo_format_t, width: cint): cint {.importc: "cairo_format_stride_for_width", libgtk3.} - -proc cairo_create*(target: pointer): pointer {.importc: "cairo_create", libgtk3.} -proc cairo_get_target*(cr: pointer): pointer {.importc: "cairo_get_target", libgtk3.} -proc cairo_set_source_rgb*(cr: pointer, red, green, blue: cdouble) {.importc: "cairo_set_source_rgb", libgtk3.} -proc cairo_set_source_surface*(cr, surface: pointer, x, y: cdouble) {.importc: "cairo_set_source_surface", libgtk3.} -proc cairo_fill*(cr: pointer) {.importc: "cairo_fill", libgtk3.} -proc cairo_stroke*(cr: pointer) {.importc: "cairo_stroke", libgtk3.} -proc cairo_rectangle*(cr: pointer, x, y, width, height: cdouble) {.importc: "cairo_rectangle", libgtk3.} -proc cairo_line_to*(cr: pointer, x, y: cdouble) {.importc: "cairo_line_to", libgtk3.} -proc cairo_move_to*(cr: pointer, x, y: cdouble) {.importc: "cairo_move_to", libgtk3.} -proc cairo_set_line_width*(cr: pointer, width: cdouble) {.importc: "cairo_set_line_width", libgtk3.} -# proc cairo_image_surface_create_from_png*(filename: cstring): pointer {.importc: "cairo_image_surface_create_from_png", libgtk3.} -proc cairo_paint*(cr: pointer) {.importc: "cairo_paint", libgtk3.} -proc cairo_scale*(cr: pointer, x, y: cdouble) {.importc: "cairo_scale", libgtk3.} -proc cairo_translate*(cr: pointer, tx, ty: cdouble) {.importc: "cairo_translate", libgtk3.} -# proc cairo_set_antialias*(cr: pointer, antialias: cint) {.importc: "cairo_set_antialias", libgtk3.} -proc cairo_save*(cr: pointer) {.importc: "cairo_save", libgtk3.} -proc cairo_restore*(cr: pointer) {.importc: "cairo_restore", libgtk3.} - -proc pango_cairo_show_layout*(cr, layout: pointer) {.importc: "pango_cairo_show_layout", libgtk3.} -proc pango_cairo_create_layout*(cr: pointer): pointer {.importc: "pango_cairo_create_layout", libgtk3.} -proc pango_layout_set_text*(layout: pointer, text: cstring, length: cint) {.importc: "pango_layout_set_text", libgtk3.} -proc pango_layout_get_pixel_size*(layout: pointer, width, height: var cint) {.importc: "pango_layout_get_pixel_size", libgtk3.} -proc pango_layout_set_font_description*(layout, desc: pointer) {.importc: "pango_layout_set_font_description", libgtk3.} -proc pango_font_description_new*(): pointer {.importc: "pango_font_description_new", libgtk3.} -proc pango_font_description_set_family*(desc: pointer, family: cstring) {.importc: "pango_font_description_set_family", libgtk3.} -proc pango_font_description_set_size*(desc: pointer, size: cint) {.importc: "pango_font_description_set_size", libgtk3.} -# proc pango_font_description_get_size*(desc: pointer): cint {.importc: "pango_font_description_get_size", libgtk3.} -# proc pango_layout_set_markup*(layout: pointer, markup: cstring, length: cint) {.importc: "pango_layout_set_markup", libgtk3.} -# proc pango_layout_new*(context: pointer): pointer {.importc: "pango_layout_new", libgtk3.} - - diff --git a/src/gtk3/nigui_platform_impl.nim b/src/gtk3/nigui_platform_impl.nim deleted file mode 100755 index fbb7b45..0000000 --- a/src/gtk3/nigui_platform_impl.nim +++ /dev/null @@ -1,1176 +0,0 @@ -# NiGui - GTK+ 3 platform-specific code - part 3 - -# This file will be included in "nigui.nim". - -# Imports: -# math, os, strutils, times are imported by nigui.nim -import gtk3 -import tables -import hashes - - -# ---------------------------------------------------------------------------------------- -# Internal Things -# ---------------------------------------------------------------------------------------- - -const pFontSizeFactor = 700 - -# needed to calculate clicks: -var pLastMouseButtonDownControl: ControlImpl -var pLastMouseButtonDownControlX: int -var pLastMouseButtonDownControlY: int - -proc pRaiseGError(error: ptr GError) = - if error == nil: - raiseError("Unkown error") - raiseError($error.message, false) - -proc pColorToGdkRGBA(color: Color, rgba: var GdkRGBA) = - rgba.red = color.red.float / 255 - rgba.green = color.green.float / 255 - rgba.blue = color.blue.float / 255 - rgba.alpha = color.alpha.float / 255 - -proc pGdkRGBAToColor(rgba: var GdkRGBA): Color = - result.red = uint8(rgba.red.float * 255) - result.green = uint8(rgba.green.float * 255) - result.blue = uint8(rgba.blue.float * 255) - result.alpha = uint8(rgba.alpha.float * 255) - -proc pWindowDeleteSignal(widgetHandle, event, data: pointer): bool {.cdecl.} = - let window = cast[WindowImpl](data) - window.dispose() - return true - -proc pWindowConfigureSignal(windowHandle, event, data: pointer): bool {.cdecl.} = - # called on resize and move - let window = cast[WindowImpl](data) - var x, y: cint - gtk_window_get_position(window.fHandle, x, y) - window.fX = x - window.fY = y - var width, height: cint - gtk_window_get_size(window.fHandle, width, height) - window.fWidth = width - window.fHeight = height - window.fClientWidth = width - window.fClientHeight = height - window.triggerRelayout() - -proc pKeyvalToKey(keyval: cint): 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 97: Key_A - of 98: Key_B - of 99: Key_C - of 100: Key_D - of 101: Key_E - of 102: Key_F - of 103: Key_G - of 104: Key_H - of 105: Key_I - of 106: Key_J - of 107: Key_K - of 108: Key_L - of 109: Key_M - of 110: Key_N - of 111: Key_O - of 112: Key_P - of 113: Key_Q - of 114: Key_R - of 115: Key_S - of 116: Key_T - of 117: Key_U - of 118: Key_V - of 119: Key_W - of 120: Key_X - of 121: Key_Y - of 122: Key_Z - of 32: Key_Space - of 65289: Key_Tab - of 65293: Key_Return - of 65307: Key_Escape - of 65379: Key_Insert - of 65535: Key_Delete - of 65288: Key_Backspace - of 65361: Key_Left - of 65362: Key_Up - of 65363: Key_Right - of 65364: Key_Down - of 65360: Key_Home - of 65367: Key_End - of 65365: Key_PageUp - of 65366: Key_PageDown - else: Key_None - -proc pWindowKeyPressSignal(widget: pointer, event: var GdkEventKey, data: pointer): bool {.cdecl.} = - # echo "window keyPressCallback" - - # echo event.keyval - # echo event.hardware_keycode - - # echo $gdk_keyval_to_unicode(event.keyval) - var unicode = gdk_keyval_to_unicode(event.keyval) - # if unicode == 0: - # unicode = event.keyval - - let window = cast[WindowImpl](data) - var evt = new WindowKeyEvent - evt.window = window - evt.key = pKeyvalToKey(event.keyval) - if evt.key == Key_None: - echo "Unkown key value: ", event.keyval - return - - evt.character = $event.`string` - evt.unicode = unicode - - try: - window.handleKeyDownEvent(evt) - except: - handleException() - -proc pControlKeyPressSignal(widget: pointer, event: var GdkEventKey, data: pointer): bool {.cdecl.} = - - # echo "control keyPressCallback" - - # echo $gdk_keyval_to_unicode(event.keyval) - var unicode = gdk_keyval_to_unicode(event.keyval) - # if unicode == 0: - # unicode = event.keyval - - let control = cast[ControlImpl](data) - var evt = new ControlKeyEvent - evt.control = control - evt.key = pKeyvalToKey(event.keyval) - if evt.key == Key_None: - echo "Unkown key value: ", event.keyval - return - evt.character = $event.`string` - evt.unicode = unicode - - try: - control.handleKeyDownEvent(evt) - except: - handleException() - - return evt.cancel - -method focus(control: ControlImpl) = - gtk_widget_grab_focus(control.fHandle) - -method focus(control: NativeTextArea) = - gtk_widget_grab_focus(control.fTextViewHandle) - -proc pDefaultControlButtonPressSignal(widget: pointer, event: var GdkEventButton, data: pointer): bool {.cdecl.} = - let control = cast[ControlImpl](data) - let x = event.x.int - let y = event.y.int - var evt = new MouseButtonEvent - evt.control = control - case event.button - of 1: evt.button = MouseButton_Left - of 2: evt.button = MouseButton_Middle - of 3: evt.button = MouseButton_Right - else: return # TODO - evt.x = x - evt.y = y - - try: - control.handleMouseButtonDownEvent(evt) - except: - handleException() - - pLastMouseButtonDownControl = control - pLastMouseButtonDownControlX = x - pLastMouseButtonDownControlY = y - -proc pCustomControlButtonPressSignal(widget: pointer, event: var GdkEventButton, data: pointer): bool {.cdecl.} = - discard pDefaultControlButtonPressSignal(widget, event, data) - let control = cast[ControlImpl](data) - control.focus() - result = true # Stop propagation, required to detect clicks - -proc pControlButtonReleaseSignal(widget: pointer, event: var GdkEventButton, data: pointer): bool {.cdecl.} = - let control = cast[ControlImpl](data) - let x = event.x.int - let y = event.y.int - if not (x >= 0 and x < control.width and y >= 0 and y < control.height): - return - var evt = new MouseButtonEvent - evt.control = control - case event.button - of 1: evt.button = MouseButton_Left - of 2: evt.button = MouseButton_Middle - of 3: evt.button = MouseButton_Right - else: return # TODO - evt.x = x - evt.y = y - control.handleMouseButtonUpEvent(evt) - if event.button == 1 and control == pLastMouseButtonDownControl and abs(x - pLastMouseButtonDownControlX) <= clickMaxXYMove and abs(y - pLastMouseButtonDownControlY) <= clickMaxXYMove: - var clickEvent = new ClickEvent - clickEvent.control = control - try: - control.handleClickEvent(clickEvent) - except: - handleException() - # result = true # stop propagation - -proc pControlChangedSignal(widget: pointer, data: pointer): bool {.cdecl.} = - let control = cast[ControlImpl](data) - var evt = new TextChangeEvent - try: - control.handleTextChangeEvent(evt) - except: - handleException() - -# proc pTextAreaEndUserActionSignal(widget: pointer, data: pointer): bool {.cdecl.} = - # let control = cast[ControlImpl](data) - # discard - -proc pSetDragDest(widget: pointer) = - var target: GtkTargetEntry - target.target = "text/uri-list" - target.flags = 0 - target.info = 0 - gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_ALL, target.addr, 1, GDK_ACTION_COPY) - - -# ---------------------------------------------------------------------------------------- -# App Procedures -# ---------------------------------------------------------------------------------------- - -proc init(app: App) = - gtk_init(nil, nil) - - # Determine default styles: - var window = gtk_window_new(GTK_WINDOW_TOPLEVEL) - var context = gtk_widget_get_style_context(window) - var rgba: GdkRGBA - gtk_style_context_get_background_color(context, GTK_STATE_FLAG_NORMAL, rgba) - app.defaultBackgroundColor = rgba.pGdkRGBAToColor() - gtk_style_context_get_color(context, GTK_STATE_FLAG_NORMAL, rgba) - app.defaultTextColor = rgba.pGdkRGBAToColor() - gtk_widget_destroy(window) - -proc runMainLoop() = gtk_main() - -proc processEvents(app: App) = - while gtk_events_pending() == 1: - discard gtk_main_iteration() - - -# ---------------------------------------------------------------------------------------- -# Dialogs -# ---------------------------------------------------------------------------------------- - -proc alert(window: Window, message: string, title = "Message") = - var dialog = gtk_dialog_new() - gtk_window_set_title(dialog, title) - gtk_window_resize(dialog, 200, 70) - let contentArea = gtk_dialog_get_content_area(dialog) - gtk_container_set_border_width(contentArea, 15) - var label = gtk_label_new(message) - gtk_widget_show(label) - gtk_box_pack_start(contentArea, label, 0, 0, 0) - let actionArea = gtk_dialog_get_action_area(dialog) - gtk_button_box_set_layout(actionArea, GTK_BUTTONBOX_EXPAND) - gtk_widget_set_margin_top(actionArea, 15) - discard gtk_dialog_add_button(dialog, "OK", 1) - if window != nil: - gtk_window_set_transient_for(dialog, cast[WindowImpl](window).fHandle) - discard gtk_dialog_run(dialog) - gtk_widget_hide(dialog) - -method run*(dialog: OpenFileDialog) = - dialog.files = @[] - var chooser = gtk_file_chooser_dialog_new(nil, nil, GTK_FILE_CHOOSER_ACTION_OPEN, "Cancel", GTK_RESPONSE_CANCEL, "Open", GTK_RESPONSE_ACCEPT, nil) - # Issue: When a title is passed, the dialog is shown without a title - discard gtk_file_chooser_set_current_folder(chooser, dialog.directory) - gtk_file_chooser_set_select_multiple(chooser, dialog.multiple) - let res = gtk_dialog_run(chooser) - if res == GTK_RESPONSE_ACCEPT: - let list = gtk_file_chooser_get_filenames(chooser) - let count = g_slist_length(list) - for i in 0..count - 1: - dialog.files.add($g_slist_nth_data_string(list, i)) - gtk_widget_destroy(chooser) - -method run(dialog: SaveFileDialog) = - var chooser = gtk_file_chooser_dialog_new(nil, nil, GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", GTK_RESPONSE_CANCEL, "Save", GTK_RESPONSE_ACCEPT, nil) - # Issue: When a title is passed, the dialog is shown without a title - let res = gtk_dialog_run(chooser) - discard gtk_file_chooser_set_current_folder(chooser, dialog.directory) - if dialog.defaultName.len > 0: - discard gtk_file_chooser_set_current_name(chooser, dialog.defaultName) # Issue: does not work - if res == GTK_RESPONSE_ACCEPT: - dialog.file = $gtk_file_chooser_get_filename(chooser) - else: - dialog.file = "" - gtk_widget_destroy(chooser) - - -# ---------------------------------------------------------------------------------------- -# Timers -# ---------------------------------------------------------------------------------------- - -type TimerEntry = object - timerInternalId: cint - timerProc: TimerProc - data: pointer - -var - pTimers = initTable[int64, TimerEntry]() - pNextTimerId: int = 1 - -proc pTimerFunction(timer: Timer): bool {.cdecl.} = - let timerEntry = pTimers.getOrDefault(cast[int](timer)) - var event = new TimerEvent - event.timer = timer - event.data = timerEntry.data - timerEntry.timerProc(event) - pTimers.del(cast[int](timer)) - # result is false to stop timer - -proc pRepeatingTimerFunction(timer: Timer): bool {.cdecl.} = - let timerEntry = pTimers.getOrDefault(cast[int](timer)) - var event = new TimerEvent - event.timer = timer - event.data = timerEntry.data - timerEntry.timerProc(event) - result = true # repeat timer - -proc startTimer(milliSeconds: int, timerProc: TimerProc, data: pointer = nil): Timer = - var timerEntry: TimerEntry - timerEntry.timerInternalId = g_timeout_add(milliSeconds.cint, pTimerFunction, cast[pointer](pNextTimerId)) - timerEntry.timerProc = timerProc - timerEntry.data = data - pTimers[pNextTimerId] = timerEntry - result = cast[Timer](pNextTimerId) - pNextTimerId.inc() - if pNextTimerId == inactiveTimer: - pNextTimerId.inc() - -proc startRepeatingTimer(milliSeconds: int, timerProc: TimerProc, data: pointer = nil): Timer = - var timerEntry: TimerEntry - timerEntry.timerInternalId = g_timeout_add(milliSeconds.cint, pRepeatingTimerFunction, cast[pointer](pNextTimerId)) - timerEntry.timerProc = timerProc - timerEntry.data = data - pTimers[pNextTimerId] = timerEntry - result = cast[Timer](pNextTimerId) - pNextTimerId.inc() - if pNextTimerId == inactiveTimer: - pNextTimerId.inc() - -proc stop(timer: var Timer) = - if cast[int](timer) != inactiveTimer: - let timerEntry = pTimers.getOrDefault(cast[int](timer)) - pTimers.del(cast[int](timer)) - discard g_source_remove(timerEntry.timerInternalId) - timer = cast[Timer](inactiveTimer) - -# ---------------------------------------------------------------------------------------- -# Canvas -# ---------------------------------------------------------------------------------------- - -proc pUpdateFont(canvas: Canvas) = - let canvasImpl = cast[CanvasImpl](canvas) - canvasImpl.fFont = pango_font_description_new() - pango_font_description_set_family(canvasImpl.fFont, canvas.fontFamily) - pango_font_description_set_size(canvasImpl.fFont, cint(canvas.fontSize * pFontSizeFactor)) - -method drawText(canvas: Canvas, text: string, x, y = 0) = - let canvasImpl = cast[CanvasImpl](canvas) - let cr = canvasImpl.fCairoContext - if cr == nil: - raiseError("Canvas is not in drawing state.") - var rgba: GdkRGBA - canvas.textColor.pColorToGdkRGBA(rgba) - gdk_cairo_set_source_rgba(cr, rgba) - - var layout = pango_cairo_create_layout(cr) - pango_layout_set_text(layout, text, text.len.cint) - - if canvasImpl.fFont == nil: - canvas.pUpdateFont() - pango_layout_set_font_description(layout, canvasImpl.fFont) - - cairo_move_to(cr, x.float, y.float) - pango_cairo_show_layout(cr, layout) - -method drawLine(canvas: Canvas, x1, y1, x2, y2: int) = - let cr = cast[CanvasImpl](canvas).fCairoContext - if cr == nil: - raiseError("Canvas is not in drawing state.") - var rgba: GdkRGBA - canvas.lineColor.pColorToGdkRGBA(rgba) - gdk_cairo_set_source_rgba(cr, rgba) - cairo_move_to(cr, x1.float, y1.float) - cairo_line_to(cr, x2.float, y2.float) - cairo_stroke(cr) - -method drawRectArea(canvas: Canvas, x, y, width, height: int) = - let cr = cast[CanvasImpl](canvas).fCairoContext - if cr == nil: - raiseError("Canvas is not in drawing state.") - var rgba: GdkRGBA - canvas.areaColor.pColorToGdkRGBA(rgba) - gdk_cairo_set_source_rgba(cr, rgba) - cairo_rectangle(cr, x.float, y.float, width.float, height.float) - cairo_fill(cr) - -method drawRectOutline(canvas: Canvas, x, y, width, height: int) = - let cr = cast[CanvasImpl](canvas).fCairoContext - if cr == nil: - raiseError("Canvas is not in drawing state.") - var rgba: GdkRGBA - canvas.lineColor.pColorToGdkRGBA(rgba) - gdk_cairo_set_source_rgba(cr, rgba) - cairo_rectangle(cr, x.float, y.float, width.float, height.float) - cairo_set_line_width(cr, 1) - cairo_stroke(cr) - -method drawImage(canvas: Canvas, image: Image, x, y = 0, width, height = -1) = - let cr = cast[CanvasImpl](canvas).fCairoContext - if cr == nil: - raiseError("Canvas is not in drawing state.") - let imageCanvas = cast[CanvasImpl](image.canvas) - if imageCanvas.fSurface == nil: - raiseError("Image is not initialized.") - var drawWith = image.width - var drawHeight = image.height - if width != -1: - drawWith = width - drawHeight = int(drawHeight * drawWith / image.width) - if height != -1: - drawHeight = height - if drawWith == image.width and drawHeight == image.height: - cairo_set_source_surface(cr, imageCanvas.fSurface, x.cdouble, y.cdouble) - cairo_paint(cr) - else: - cairo_save(cr) - cairo_translate(cr, x.cdouble, y.cdouble) - cairo_scale(cr, drawWith / image.width, drawHeight / image.height) - cairo_set_source_surface(cr, imageCanvas.fSurface, 0, 0) - cairo_paint(cr) - cairo_restore(cr) - -method setPixel(canvas: Canvas, x, y: int, color: Color) = - let canvasImpl = cast[CanvasImpl](canvas) - let cr = canvasImpl.fCairoContext - if cr == nil: - raiseError("Canvas is not in drawing state.") - if canvasImpl.fData == nil: - # For a Canvas of a Control we can't access the memory directly, so draw a rectangle (slower) - var rgba: GdkRGBA - color.pColorToGdkRGBA(rgba) - gdk_cairo_set_source_rgba(cr, rgba) - cairo_rectangle(cr, x.float, y.float, 1, 1) - cairo_fill(cr) - else: - # For a Canvas of an Image we can write in the memory directly (faster) - if x < 0 or y < 0 or x >= canvas.width or y >= canvas.height: - raiseError("Pixel is out of range.") - cairo_surface_flush(canvasImpl.fSurface) - let i = y * canvasImpl.fStride + x * 4 - canvasImpl.fData[i + 0] = color.blue.chr - canvasImpl.fData[i + 1] = color.green.chr - canvasImpl.fData[i + 2] = color.red.chr - canvasImpl.fData[i + 3] = 255.chr - cairo_surface_mark_dirty(canvasImpl.fSurface) - -method `fontFamily=`(canvas: CanvasImpl, fontFamily: string) = - procCall canvas.Canvas.`fontFamily=`(fontFamily) - canvas.fFont = nil - -method `fontSize=`(canvas: CanvasImpl, fontSize: int) = - procCall canvas.Canvas.`fontSize=`(fontSize) - canvas.fFont = nil - -method getTextLineWidth(canvas: CanvasImpl, text: string): int = - if canvas.fCairoContext == nil: - raiseError("Canvas is not in drawing state.") - var layout = pango_cairo_create_layout(canvas.fCairoContext) - pango_layout_set_text(layout, text, text.len.cint) - if canvas.fFont == nil: - canvas.pUpdateFont() - pango_layout_set_font_description(layout, canvas.fFont) - var width: cint = 0 - var height: cint = 0 - pango_layout_get_pixel_size(layout, width, height) - result = width - -method getTextLineHeight(canvas: CanvasImpl): int = - if canvas.fCairoContext == nil: - raiseError("Canvas is not in drawing state.") - var layout = pango_cairo_create_layout(canvas.fCairoContext) - pango_layout_set_text(layout, "a", 1) - if canvas.fFont == nil: - canvas.pUpdateFont() - pango_layout_set_font_description(layout, canvas.fFont) - var width: cint = 0 - var height: cint = 0 - pango_layout_get_pixel_size(layout, width, height) - result = height - - -# ---------------------------------------------------------------------------------------- -# Image -# ---------------------------------------------------------------------------------------- - -method resize(image: Image, width, height: int) = - let canvas = cast[CanvasImpl](image.fCanvas) - if canvas.fSurface != nil: - cairo_surface_destroy(canvas.fSurface) - canvas.fSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width.cint, height.cint) - canvas.fCairoContext = cairo_create(canvas.fSurface) - canvas.fData = cairo_image_surface_get_data(canvas.fSurface) - canvas.fStride = cairo_image_surface_get_stride(canvas.fSurface) - image.canvas.fWidth = width - image.canvas.fHeight = height - -method loadFromFile(image: Image, filePath: string) = - let canvas = cast[CanvasImpl](image.fCanvas) - if canvas.fSurface != nil: - cairo_surface_destroy(canvas.fSurface) - var error: ptr GError - var pixbuf = gdk_pixbuf_new_from_file(filePath, error.addr) - if pixbuf == nil: - pRaiseGError(error) - canvas.fSurface = gdk_cairo_surface_create_from_pixbuf(pixbuf, 1, nil) - canvas.fCairoContext = cairo_create(canvas.fSurface) - canvas.fData = cairo_image_surface_get_data(canvas.fSurface) - canvas.fStride = cairo_image_surface_get_stride(canvas.fSurface) - image.canvas.fWidth = cairo_image_surface_get_width(canvas.fSurface) - image.canvas.fHeight = cairo_image_surface_get_height(canvas.fSurface) - -method saveToPngFile(image: Image, filePath: string) = - let canvas = cast[CanvasImpl](image.fCanvas) - var pixbuf = gdk_pixbuf_get_from_surface(canvas.fSurface, 0, 0, image.width.cint, image.height.cint) - var error: ptr GError - if not gdk_pixbuf_save(pixbuf, filePath, "png", error.addr, nil, nil, nil): - pRaiseGError(error) - -method saveToJpegFile(image: Image, filePath: string, quality = 80) = - let canvas = cast[CanvasImpl](image.fCanvas) - var pixbuf = gdk_pixbuf_get_from_surface(canvas.fSurface, 0, 0, image.width.cint, image.height.cint) - var error: ptr GError - if not gdk_pixbuf_save(pixbuf, filePath, "jpeg", error.addr, "quality", $quality, nil): - pRaiseGError(error) - - -# ---------------------------------------------------------------------------------------- -# Window -# ---------------------------------------------------------------------------------------- - -proc pWindowDragDataReceivedSignal(widget, context: pointer, x, y: cint, data: pointer, info, time: cint, user_data: pointer) {.cdecl.} = - let window = cast[WindowImpl](user_data) - var files: seq[string] = @[] - var p = gtk_selection_data_get_uris(data) - while p[] != nil: - files.add($g_filename_from_uri(p[])) - p = cast[ptr cstring](cast[int](p) + 8) - var event = new DropFilesEvent - event.window = window - event.files = files - window.handleDropFilesEvent(event) - -proc pMainScrollbarDraw(widget: pointer, cr: pointer, data: pointer): bool {.cdecl.} = - # This proc is there to get the scrollbar size - if fScrollbarSize == -1: - var scrollbar = gtk_scrolled_window_get_hscrollbar(widget) - var allocation: GdkRectangle - gtk_widget_get_allocation(scrollbar, allocation) - gtk_scrolled_window_set_policy(widget, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC) - fScrollbarSize = allocation.height - -proc init(window: WindowImpl) = - window.fHandle = gtk_window_new(GTK_WINDOW_TOPLEVEL) - window.fInnerHandle = gtk_scrolled_window_new(nil, nil) - gtk_widget_show(window.fInnerHandle) - gtk_container_add(window.fHandle, window.fInnerHandle) - window.Window.init() - discard g_signal_connect_data(window.fHandle, "delete-event", pWindowDeleteSignal, cast[pointer](window)) - discard g_signal_connect_data(window.fHandle, "configure-event", pWindowConfigureSignal, cast[pointer](window)) - discard g_signal_connect_data(window.fHandle, "key-press-event", pWindowKeyPressSignal, cast[pointer](window)) - - # Enable drag and drop of files: - pSetDragDest(window.fHandle) - discard g_signal_connect_data(window.fHandle, "drag-data-received", pWindowDragDataReceivedSignal, cast[pointer](window)) - - if fScrollbarSize == -1: - gtk_scrolled_window_set_policy(window.fInnerHandle, GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS) - discard g_signal_connect_data(window.fInnerHandle, "draw", pMainScrollbarDraw, nil) - -method destroy(window: WindowImpl) = - procCall window.Window.destroy() - gtk_widget_destroy(window.fHandle) - # this destroys also child widgets - window.fHandle = nil - -method `visible=`(window: WindowImpl, visible: bool) = - procCall window.Window.`visible=`(visible) - if visible: - gtk_widget_show(window.fHandle) - while fScrollbarSize == -1: - discard gtk_main_iteration() - else: - gtk_widget_hide(window.fHandle) - app.processEvents() - -method showModal(window, parent: WindowImpl) = - gtk_window_set_modal(window.fHandle, 1) - gtk_window_set_transient_for(window.fHandle, parent.fHandle) - window.visible = true - -method `width=`*(window: WindowImpl, width: int) = - procCall window.Window.`width=`(width) - gtk_window_resize(window.fHandle, window.width.cint, window.height.cint) - window.fClientWidth = window.width - -method `height=`*(window: WindowImpl, height: int) = - procCall window.Window.`height=`(height) - gtk_window_resize(window.fHandle, window.width.cint, window.height.cint) - window.fClientHeight = window.height - -proc pUpdatePosition(window: WindowImpl) = gtk_window_move(window.fHandle, window.x.cint, window.y.cint) - -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 screen = gdk_screen_get_default() - let monitor = gdk_screen_get_primary_monitor(screen) - var rect: GdkRectangle - gdk_screen_get_monitor_workarea(screen, monitor, rect) - window.fX = rect.x + (rect.width - window.width) div 2 - window.fY = rect.y + (rect.height - window.height) div 2 - window.pUpdatePosition() - -method `title=`(window: WindowImpl, title: string) = - procCall window.Window.`title=`(title) - gtk_window_set_title(window.fHandle, window.title.cstring) - -method `control=`(window: WindowImpl, control: ControlImpl) = - procCall window.Window.`control=`(control) - gtk_container_add(window.fInnerHandle, control.fHandle) - -method `iconPath=`(window: WindowImpl, iconPath: string) = - procCall window.Window.`iconPath=`(iconPath) - gtk_window_set_icon_from_file(window.fHandle, iconPath, nil) - - -# ---------------------------------------------------------------------------------------- -# Control -# ---------------------------------------------------------------------------------------- - -method pUpdateScrollBar(control: ControlImpl) - -proc pControlDrawSignal(widget: pointer, cr: pointer, data: pointer): bool {.cdecl.} = - let control = cast[ControlImpl](data) - - # Trigger pUpdateScrollBar() in case it's not initialized, which could be because of missing fScrollbarSize - if control.fHScrollbar == nil: - control.pUpdateScrollBar() - - var event = new DrawEvent - event.control = control - var canvas = cast[CanvasImpl](control.canvas) - if canvas == nil: - canvas = newCanvas(control) - canvas.fCairoContext = cr - # canvas.fSurface = cairo_get_target(cr) # no need to set this - # canvas.fData = cairo_image_surface_get_data(canvas.fSurface) # does not work - try: - # var region = gdk_window_get_clip_region(control.fHandle) - # gdk_window_begin_paint_region(control.fHandle, region) - # no effect - - control.handleDrawEvent(event) - - # gdk_window_end_paint(control.fHandle) - except: - handleException() - - canvas.fCairoContext = nil - -proc pControlScollXSignal(adjustment: pointer, data: pointer) {.cdecl.} = - let control = cast[ControlImpl](data) - control.fXScrollPos = gtk_adjustment_get_value(adjustment).int - control.forceRedraw() - -proc pControlScollYSignal(adjustment: pointer, data: pointer) {.cdecl.} = - let control = cast[ControlImpl](data) - control.fYScrollPos = gtk_adjustment_get_value(adjustment).int - control.forceRedraw() - -proc pUpdateFont(control: ControlImpl) = - var font = pango_font_description_new() - pango_font_description_set_family(font, control.fontFamily) - pango_font_description_set_size(font, cint(control.fontSize * pFontSizeFactor)) - gtk_widget_modify_font(control.fHandle, font) - var rgba: GdkRGBA - control.textColor.pColorToGdkRGBA(rgba) - gtk_widget_override_color(control.fHandle, GTK_STATE_FLAG_NORMAL, rgba) - -method pAddButtonPressEvent(control: ControlImpl) = - gtk_widget_add_events(control.fHandle, GDK_BUTTON_PRESS_MASK) - discard g_signal_connect_data(control.fHandle, "button-press-event", pCustomControlButtonPressSignal, cast[pointer](control)) - -proc init(control: ControlImpl) = - - if control.fHandle == nil: - # Direct instance of ControlImpl: - control.fHandle = gtk_layout_new(nil, nil) - discard g_signal_connect_data(control.fHandle, "draw", pControlDrawSignal, cast[pointer](control)) - - gtk_widget_add_events(control.fHandle, GDK_KEY_PRESS_MASK) - discard g_signal_connect_data(control.fHandle, "key-press-event", pControlKeyPressSignal, cast[pointer](control)) - - control.pAddButtonPressEvent() - - gtk_widget_add_events(control.fHandle, GDK_BUTTON_RELEASE_MASK) - discard g_signal_connect_data(control.fHandle, "button-release-event", pControlButtonReleaseSignal, cast[pointer](control)) - - procCall control.Control.init() - -method destroy(control: ControlImpl) = - procCall control.Control.destroy() - gtk_widget_destroy(control.fHandle) - # this destroys also child widgets - -method `visible=`(control: ControlImpl, visible: bool) = - procCall control.Control.`visible=`(visible) - if visible: - gtk_widget_show(control.fHandle) - else: - gtk_widget_hide(control.fHandle) - -proc dummy(widget: pointer, event: var GdkEventButton, data: pointer): bool {.cdecl.} = - echo "dummy" - result = true # Stop propagation - -method pUpdateScrollBar(control: ControlImpl) = - if control.fScrollableWidth == -1 and control.fScrollableHeight == -1: - return - echo "control.pUpdateScrollBar" - if control.fHScrollbar == nil: - if fScrollbarSize == -1: - return - # Init scrolling: - # echo "fScrollbarSize ", fScrollbarSize - control.fHAdjust = gtk_adjustment_new(0, 0, 0, 10, 10, 0) - control.fVAdjust = gtk_adjustment_new(0, 0, 0, 10, 10, 0) - control.fHScrollbar = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, control.fHAdjust) - control.fVScrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, control.fVAdjust) - gtk_container_add(control.fHandle, control.fHScrollbar) - gtk_container_add(control.fHandle, control.fVScrollbar) - discard g_signal_connect_data(control.fHAdjust, "value-changed", pControlScollXSignal, cast[pointer](control)) - discard g_signal_connect_data(control.fVAdjust, "value-changed", pControlScollYSignal, cast[pointer](control)) - - # The dead corner is an area which just needs to be covered with a control without function and the default background color - control.fDeadCornerHandle = gtk_label_new("") - # control.fDeadCornerHandle = gtk_fixed_new() - gtk_container_add(control.fHandle, control.fDeadCornerHandle) - var rgba: GdkRGBA - pColorToGdkRGBA(app.defaultBackgroundColor, rgba) - gtk_widget_override_background_color(control.fDeadCornerHandle, GTK_STATE_FLAG_NORMAL, rgba) - gtk_widget_set_size_request(control.fDeadCornerHandle, fScrollbarSize.cint, fScrollbarSize.cint) - - # Prevent that a click on the dead corner triggers the onClick event of the Control: - # gtk_widget_add_events(control.fDeadCornerHandle, GDK_BUTTON_PRESS_MASK) - # discard g_signal_connect_data(control.fDeadCornerHandle, "button-press-event", dummy, nil) - # gtk_widget_add_events(control.fDeadCornerHandle, GDK_BUTTON_RELEASE_MASK) - # discard g_signal_connect_data(control.fDeadCornerHandle, "button-release-event", dummy, nil) - # TODO: does not work. try EventBox - - # 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 - - # Update horizontal scrollbar: - if control.fXScrollEnabled: - var upper = control.scrollableWidth - var size = control.width - if control.fYScrollEnabled: - upper.inc(fScrollbarSize) - size.dec(fScrollbarSize) - gtk_adjustment_set_upper(control.fHAdjust, upper.cdouble) - let value = gtk_adjustment_get_value(control.fHAdjust).int - let maxValue = upper - control.width - if value > maxValue: - gtk_adjustment_set_value(control.fHAdjust, maxValue.float) - gtk_adjustment_set_page_size(control.fHAdjust, control.width.float) - gtk_widget_set_size_request(control.fHScrollbar, size.cint, 0) - gtk_layout_move(control.fHandle, control.fHScrollbar, 0, (control.height - fScrollbarSize).cint) - gtk_widget_show(control.fHScrollbar) - # Ensure that scroll pos is within range: - control.fXScrollPos = max(min(control.fXScrollPos, maxValue), 0) - else: - gtk_widget_hide(control.fHScrollbar) - control.fXScrollPos = 0 - - # Update vertical scrollbar: - if control.fYScrollEnabled: - var upper = control.scrollableHeight - var size = control.height - if control.fXScrollEnabled: - upper.inc(fScrollbarSize) - size.dec(fScrollbarSize) - gtk_adjustment_set_upper(control.fVAdjust, upper.cdouble) - let value = gtk_adjustment_get_value(control.fVAdjust).int - let maxValue = upper - control.height - if value > maxValue: - gtk_adjustment_set_value(control.fVAdjust, maxValue.float) - gtk_adjustment_set_page_size(control.fVAdjust, control.height.float) - gtk_widget_set_size_request(control.fVScrollbar, 0, size.cint) - gtk_layout_move(control.fHandle, control.fVScrollbar, (control.width - fScrollbarSize).cint, 0) - gtk_widget_show(control.fVScrollbar) - # Ensure that scroll pos is within range: - control.fYScrollPos = max(min(control.fYScrollPos, maxValue), 0) - else: - gtk_widget_hide(control.fVScrollbar) - control.fYScrollPos = 0 - - # Update dead corner: - if control.fXScrollEnabled and control.fYScrollEnabled: - gtk_layout_move(control.fHandle, control.fDeadCornerHandle, (control.width - fScrollbarSize).cint, (control.height - fScrollbarSize).cint) - gtk_widget_show(control.fDeadCornerHandle) - else: - gtk_widget_hide(control.fDeadCornerHandle) - - -method setSize(control: ControlImpl, width, height: int) = - if width == control.fWidth and height == control.fHeight: - return - procCall control.Control.setSize(width, height) - gtk_widget_set_size_request(control.fHandle, width.cint, height.cint) - pUpdateScrollBar(control) - -method setPosition(control: ControlImpl, x, y: int) = - procCall control.Control.setPosition(x, y) - if control.fParentControl != nil: - gtk_fixed_move(cast[ContainerImpl](control.fParentControl).fInnerHandle, control.fHandle, x.cint, y.cint) - # gtk_layout_move(cast[ContainerImpl](control.fParentControl).fHandle, control.fHandle, x.cint, y.cint) - -method forceRedraw(control: ControlImpl) = gtk_widget_queue_draw(control.fHandle) - -# proc removeWidgetInternal(container: WidgetContainer, widget: Widget) = gtk_container_remove(container.innerHandle, widget.handle) - -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 setTextColor(control: ControlImpl, color: Color) = - procCall control.Control.setTextColor(color) - control.pUpdateFont() - -method `setBackgroundColor=`(control: ControlImpl, color: Color) = - procCall control.Control.setBackgroundColor(color) - var rgba: GdkRGBA - color.pColorToGdkRGBA(rgba) - gtk_widget_override_background_color(control.fHandle, GTK_STATE_FLAG_NORMAL, rgba) - # TODO: check why it has no effect - -method getTextLineWidth(control: ControlImpl, text: string): int = - var layout = gtk_widget_create_pango_layout(control.fHandle, text) - var width: cint = 0 - var height: cint = 0 - pango_layout_get_pixel_size(layout, width, height) - result = width - -method getTextLineHeight(control: ControlImpl): int = - var layout = gtk_widget_create_pango_layout(control.fHandle, "a") - var width: cint = 0 - var height: cint = 0 - pango_layout_get_pixel_size(layout, width, height) - result = height - -discard """ method `xScrollPos=`(control: ControlImpl, xScrollPos: int) = - procCall control.Control.`xScrollPos=`(xScrollPos) - control.pUpdateScrollBar() - -method `yScrollPos=`(control: ControlImpl, yScrollPos: int) = - procCall control.Control.`yScrollPos=`(yScrollPos) - control.pUpdateScrollBar() """ - -method `scrollableWidth=`(control: ControlImpl, scrollableWidth: int) = - if scrollableWidth == control.fScrollableWidth: - return - procCall control.Control.`scrollableWidth=`(scrollableWidth) - control.pUpdateScrollBar() - -method `scrollableHeight=`(control: ControlImpl, scrollableHeight: int) = - if scrollableHeight == control.fScrollableHeight: - return - procCall control.Control.`scrollableHeight=`(scrollableHeight) - control.pUpdateScrollBar() - - -# ---------------------------------------------------------------------------------------- -# Container -# ---------------------------------------------------------------------------------------- - -proc init(container: ContainerImpl) = - container.fHandle = gtk_fixed_new() - # ScrollWnd: - container.fScrollWndHandle = gtk_scrolled_window_new(nil, nil) - gtk_widget_show(container.fScrollWndHandle) - gtk_container_add(container.fHandle, container.fScrollWndHandle) - # Inner: - container.fInnerHandle = gtk_fixed_new() - gtk_widget_show(container.fInnerHandle) - gtk_container_add(container.fScrollWndHandle, container.fInnerHandle) - container.Container.init() - -method pUpdateScrollWnd(container: ContainerImpl) = - let padding = container.getPadding() - let width = container.width - padding.left - padding.right - let height = container.height - padding.top - padding.bottom - gtk_widget_set_size_request(container.fScrollWndHandle, width.cint, height.cint) - gtk_fixed_move(container.fHandle, container.fScrollWndHandle, padding.left.cint, padding.top.cint) - -method `frame=`(container: ContainerImpl, frame: Frame) = - procCall container.Container.`frame=`(frame) - if frame != nil: - gtk_container_add(container.fHandle, frame.fHandle) - container.pUpdateScrollWnd() - -method add(container: ContainerImpl, control: ControlImpl) = - gtk_container_add(container.fInnerHandle, control.fHandle) - procCall container.Container.add(control) - -method paddingLeft(container: ContainerImpl): int = 5 # TODO -method paddingRight(container: ContainerImpl): int = 5 # TODO -method paddingTop(container: ContainerImpl): int = 15 # TODO -method paddingBottom(container: ContainerImpl): int = 5 # TODO - -method setInnerSize(container: ContainerImpl, width, height: int) = - procCall container.Container.setInnerSize(width, height) - gtk_widget_set_size_request(container.fInnerHandle, width.cint, height.cint) - -method setSize(container: ContainerImpl, width, height: int) = - procCall container.Container.setSize(width, height) - container.pUpdateScrollWnd() - -method pUpdateScrollBar(container: ContainerImpl) = - # Overwrite base method - if container.fScrollableWidth == -1 and container.fScrollableHeight == -1: - return - # echo "container.pUpdateScrollBar" - - var xPolicy: cint = GTK_POLICY_NEVER - var yPolicy: cint = GTK_POLICY_NEVER - if container.fXScrollEnabled: - xPolicy = GTK_POLICY_AUTOMATIC - if container.fYScrollEnabled: - yPolicy = GTK_POLICY_AUTOMATIC - gtk_scrolled_window_set_policy(container.fScrollWndHandle, xPolicy, yPolicy) - - -# ---------------------------------------------------------------------------------------- -# Frame -# ---------------------------------------------------------------------------------------- - -proc init(frame: NativeFrame) = - frame.fHandle = gtk_frame_new("") - frame.Frame.init() - -method `text=`(frame: NativeFrame, text: string) = - procCall frame.Frame.`text=`(text) - gtk_frame_set_label(frame.fHandle, text) - -method getPadding(frame: NativeFrame): Spacing = - result = procCall frame.Frame.getPadding() - result.top = frame.getTextLineHeight() * frame.text.countLines + 2 - - -# ---------------------------------------------------------------------------------------- -# Button -# ---------------------------------------------------------------------------------------- - -proc init(button: NativeButton) = - button.fHandle = gtk_button_new() - button.Button.init() - # discard g_signal_connect_data(button.fHandle, "clicked", pWidgetClickSignal, cast[pointer](button)) - -method `text=`(button: NativeButton, text: string) = - procCall button.Button.`text=`(text) - gtk_button_set_label(button.fHandle, text) - # Don't let the button expand: - let list = gtk_container_get_children(button.fHandle) - if list != nil: - gtk_label_set_ellipsize(list.data, PANGO_ELLIPSIZE_END) - app.processEvents() - -method naturalWidth(button: NativeButton): int = - # Override parent method, to make it big enough for the text to fit in. - var context = gtk_widget_get_style_context(button.fHandle) - var padding: GtkBorder - gtk_style_context_get_padding(context, GTK_STATE_FLAG_NORMAL, padding) - result = button.getTextLineWidth(button.text) + padding.left + padding.right + 5 - -method pAddButtonPressEvent(control: NativeButton) = - gtk_widget_add_events(control.fHandle, GDK_BUTTON_PRESS_MASK) - discard g_signal_connect_data(control.fHandle, "button-press-event", pDefaultControlButtonPressSignal, cast[pointer](control)) - - -# ---------------------------------------------------------------------------------------- -# Label -# ---------------------------------------------------------------------------------------- - -proc init(label: NativeLabel) = - label.fHandle = gtk_label_new("") - gtk_label_set_xalign(label.fHandle, 0) - gtk_label_set_yalign(label.fHandle, 0.5) - gtk_label_set_ellipsize(label.fHandle, PANGO_ELLIPSIZE_END) - label.Label.init() - -method `text=`(label: NativeLabel, text: string) = - procCall label.Label.`text=`(text) - gtk_label_set_text(label.fHandle, text) - app.processEvents() - -method naturalWidth(label: NativeLabel): int = label.getTextLineWidth(label.text) + 10 -# Override parent method, to make it big enough for the text to fit in. - -method pAddButtonPressEvent(control: NativeLabel) = - gtk_widget_add_events(control.fHandle, GDK_BUTTON_PRESS_MASK) - discard g_signal_connect_data(control.fHandle, "button-press-event", pDefaultControlButtonPressSignal, cast[pointer](control)) - - -# ---------------------------------------------------------------------------------------- -# TextBox -# ---------------------------------------------------------------------------------------- - -proc init(textBox: NativeTextBox) = - textBox.fHandle = gtk_entry_new() - discard g_signal_connect_data(textBox.fHandle, "changed", pControlChangedSignal, cast[pointer](textBox)) - textBox.TextBox.init() - -method text(textBox: NativeTextBox): string = $gtk_entry_get_text(textBox.fHandle) - -method `text=`(textBox: NativeTextBox, text: string) = - gtk_entry_set_text(textBox.fHandle, text) - app.processEvents() - -method naturalHeight(textBox: NativeTextBox): int = textBox.getTextLineHeight() + 12 # add padding - -method setSize(textBox: NativeTextBox, width, height: int) = - gtk_entry_set_width_chars(textBox.fHandle, 1) - procCall textBox.ControlImpl.setSize(width, height) - -method pAddButtonPressEvent(control: NativeTextBox) = - gtk_widget_add_events(control.fHandle, GDK_BUTTON_PRESS_MASK) - discard g_signal_connect_data(control.fHandle, "button-press-event", pDefaultControlButtonPressSignal, cast[pointer](control)) - - -# ---------------------------------------------------------------------------------------- -# TextArea -# ---------------------------------------------------------------------------------------- - -proc init(textArea: NativeTextArea) = - textArea.fHandle = gtk_scrolled_window_new(nil, nil) - # gtk_scrolled_window_set_policy(textArea.fHandle, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC) - gtk_scrolled_window_set_policy(textArea.fHandle, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC) - textArea.fTextViewHandle = gtk_text_view_new() - gtk_text_view_set_left_margin(textArea.fTextViewHandle, 5) - gtk_text_view_set_right_margin(textArea.fTextViewHandle, 5) - gtk_text_view_set_top_margin(textArea.fTextViewHandle, 5) - gtk_text_view_set_bottom_margin(textArea.fTextViewHandle, 5) - gtk_container_add(textArea.fHandle, textArea.fTextViewHandle) - gtk_widget_show(textArea.fTextViewHandle) - discard g_signal_connect_data(textArea.fTextViewHandle, "key-press-event", pControlKeyPressSignal, cast[pointer](textArea)) - textArea.fBufferHandle = gtk_text_view_get_buffer(textArea.fTextViewHandle) - discard g_signal_connect_data(textArea.fBufferHandle, "changed", pControlChangedSignal, cast[pointer](textArea)) - textArea.TextArea.init() - -method text(textArea: NativeTextArea): string = - var startIter, endIter: GtkTextIter - gtk_text_buffer_get_start_iter(textArea.fBufferHandle, startIter) - gtk_text_buffer_get_end_iter(textArea.fBufferHandle, endIter) - result = $gtk_text_buffer_get_text(textArea.fBufferHandle, startIter, endIter, 1) - -method `text=`(textArea: NativeTextArea, text: string) = - gtk_text_buffer_set_text(textArea.fBufferHandle, text, text.len.cint) - app.processEvents() - -method addText(textArea: NativeTextArea, text: string) = - # overide base method for better performance and to prevent automatic scrolling to top - var iter: GtkTextIter - gtk_text_buffer_get_end_iter(textArea.fBufferHandle, iter) - gtk_text_buffer_insert(textArea.fBufferHandle, iter, text, text.len.cint) - app.processEvents() - -method scrollToBottom(textArea: NativeTextArea) = - var iter: GtkTextIter - gtk_text_buffer_get_end_iter(textArea.fBufferHandle, iter) - gtk_text_view_scroll_to_iter(textArea.fTextViewHandle, iter, 0, false, 0, 0) - app.processEvents() - -method pAddButtonPressEvent(control: NativeTextArea) = - gtk_widget_add_events(control.fHandle, GDK_BUTTON_PRESS_MASK) - discard g_signal_connect_data(control.fHandle, "button-press-event", pDefaultControlButtonPressSignal, cast[pointer](control)) - -method `wrap=`(textArea: NativeTextArea, wrap: bool) = - procCall textArea.TextArea.`wrap=`(wrap) - if wrap: - gtk_text_view_set_wrap_mode(textArea.fTextViewHandle, GTK_WRAP_WORD_CHAR) - else: - gtk_text_view_set_wrap_mode(textArea.fTextViewHandle, GTK_WRAP_NONE) - diff --git a/src/gtk3/nigui_platform_types1.nim b/src/gtk3/nigui_platform_types1.nim deleted file mode 100755 index d0178cd..0000000 --- a/src/gtk3/nigui_platform_types1.nim +++ /dev/null @@ -1,25 +0,0 @@ -# NiGui - GTK+ 3 platform-specific code - part 1 - -# This file will be included in "nigui.nim". - -type - WindowImpl* = ref object of Window - fHandle: pointer - fInnerHandle: pointer - - ControlImpl* = ref object of Control - fHandle: pointer - fHScrollbar: pointer - fVScrollbar: pointer - fHAdjust: pointer - fVAdjust: pointer - fDeadCornerHandle: pointer - - CanvasImpl* = ref object of Canvas - fSurface: pointer - fData: cstring - fStride: int - fCairoContext: pointer - fFont: pointer - - ImageImpl* = ref object of Image diff --git a/src/gtk3/nigui_platform_types2.nim b/src/gtk3/nigui_platform_types2.nim deleted file mode 100755 index c176ffc..0000000 --- a/src/gtk3/nigui_platform_types2.nim +++ /dev/null @@ -1,20 +0,0 @@ -# NiGui - GTK+ 3 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 - fTextViewHandle: pointer - fBufferHandle: pointer diff --git a/src/msgbox.nim b/src/msgbox.nim new file mode 100755 index 0000000..5071dfb --- /dev/null +++ b/src/msgbox.nim @@ -0,0 +1,82 @@ +# NiGui - extended message box + +# This module provides an extended message box. +# The message box is shown as modal window. +# The message box can have up to 3 buttons with customizable titles. +# Call the proc msgBox() to open the message box. +# It will wait until the message box is closed. +# Meaning of the result value: +# 0 - message box was closed over the window close button +# 1..3 - button 1..3 clicked + +# For an example see "example_04_msgboxes.nim". + +import nigui + +type MessageBoxWindow = ref object of WindowImpl + clickedButton: Button + +proc buttonClick(event: ClickEvent) = + cast[MessageBoxWindow](event.control.parentWindow).clickedButton = cast[Button](event.control) + event.control.parentWindow.dispose() + +proc msgBox*(parent: Window, message: string, title = "Message", button1 = "OK", button2, button3: string = nil): int {.discardable.} = + const buttonMinWidth = 100 + var window = new MessageBoxWindow + window.init() + window.title = title + + var container = newLayoutContainer(Layout_Vertical) + container.padding = 10 + window.control = container + + var labelContainer = newLayoutContainer(Layout_Horizontal) + container.add(labelContainer) + labelContainer.widthMode = WidthMode_Expand + labelContainer.heightMode = HeightMode_Expand + + var label = newLabel(message) + labelContainer.add(label) + + var buttonContainer = newLayoutContainer(Layout_Horizontal) + buttonContainer.widthMode = WidthMode_Expand + buttonContainer.xAlign = XAlign_Center + buttonContainer.spacing = 12 + container.add(buttonContainer) + var b1, b2, b3: Button + + b1 = newButton(button1) + b1.minWidth = buttonMinWidth + b1.onClick = buttonClick + buttonContainer.add(b1) + + if button2 != nil: + b2 = newButton(button2) + b2.minWidth = buttonMinWidth + b2.onClick = buttonClick + buttonContainer.add(b2) + + if button3 != nil: + b3 = newButton(button3) + b3.minWidth = buttonMinWidth + b3.onClick = buttonClick + buttonContainer.add(b3) + + window.width = min(max(label.width + 40, buttonMinWidth * 3 + 65), 600) + window.height = min(label.height, 300) + buttonContainer.height + 70 + + # Center message box on window: + window.x = parent.x + ((parent.width - window.width) div 2) + window.y = parent.y + ((parent.height - window.height) div 2) + + window.showModal(parent) + + while not window.disposed: + app.sleep(100) + + if window.clickedButton == b1: + result = 1 + elif window.clickedButton == b2: + result = 2 + elif window.clickedButton == b3: + result = 3 diff --git a/src/nigui.nim b/src/nigui.nim new file mode 100755 index 0000000..4ecdebd --- /dev/null +++ b/src/nigui.nim @@ -0,0 +1,2254 @@ +# NiGui - main file + +# This file contains all common code except extra widgets. +# All public procedures are declared here. +# Platform-specific code will added by "include". + +# Templates for "include": +template useWindows(): bool = defined(windows) and not defined(forceGtk) +template useGtk(): bool = not useWindows() + +# ======================================================================================== +# +# Public Declaration +# +# ======================================================================================== + +# ---------------------------------------------------------------------------------------- +# Simple Types +# ---------------------------------------------------------------------------------------- + +type + Layout* = enum + Layout_Horizontal + Layout_Vertical + + XAlign* = enum + XAlign_Left + XAlign_Right + XAlign_Center + XAlign_Spread + + YAlign* = enum + YAlign_Top + YAlign_Bottom + YAlign_Center + YAlign_Spread + + WidthMode* = enum + WidthMode_Static + WidthMode_Auto + WidthMode_Fill + WidthMode_Expand + + HeightMode* = enum + HeightMode_Static + HeightMode_Auto + HeightMode_Fill + HeightMode_Expand + + MouseButton* = enum + MouseButton_Left + MouseButton_Middle + MouseButton_Right + + Color* = object + red*: byte + green*: byte + blue*: byte + alpha*: byte + + Spacing* = object + left*: int + right*: int + top*: int + bottom*: int + + Timer* = distinct int + + Key* = enum + Key_None + Key_Number0 + Key_Number1 + Key_Number2 + Key_Number3 + Key_Number4 + Key_Number5 + Key_Number6 + Key_Number7 + Key_Number8 + Key_Number9 + Key_A + Key_B + Key_C + Key_D + Key_E + Key_F + Key_G + Key_H + Key_I + Key_J + Key_K + Key_L + Key_M + Key_N + Key_O + Key_P + Key_Q + Key_R + Key_S + Key_T + Key_U + Key_V + Key_W + Key_X + Key_Y + Key_Z + Key_Space + Key_Tab + Key_Return + Key_Escape + Key_Insert + Key_Delete + Key_Backspace + Key_Left + Key_Right + Key_Up + Key_Down + Key_Home + Key_End + Key_PageUp + Key_PageDown + +const inactiveTimer* = 0 + + +# ---------------------------------------------------------------------------------------- +# Widget Types 1/3 +# ---------------------------------------------------------------------------------------- + +type + # Window base type: + + Window* = ref object of RootObj + fDisposed: bool + fTitle: string + fVisible: bool + fWidth, fHeight: int + fClientWidth, fClientHeight: int + fX, fY: int + fControl: Control + fIconPath: string + fOnDispose: WindowDisposeProc + fOnResize: ResizeProc + fOnDropFiles: DropFilesProc + fOnKeyDown: WindowKeyProc + + # Control base type: + + Control* = ref object of RootObj + fDisposed: bool + fParentControl: Control # is nil or object of ContainerImpl + fParentWindow: Window # only set for top level widget + fIndex: int + fVisible: bool + fWidth, fHeight: int + fX, fY: int + fWidthMode: WidthMode + fHeightMode: HeightMode + fMinWidth, fMinHeight: int + fMaxWidth, fMaxHeight: int + fXScrollEnabled, fYScrollEnabled: bool + fXScrollPos, fYScrollPos: int + fScrollableWidth, fScrollableHeight: int + fFontFamily: string + fFontSize: int + fTextColor: Color + fBackgroundColor: Color + fUseDefaultFontFamily: bool + fUseDefaultFontSize: bool + fUseDefaultTextColor: bool + fUseDefaultBackgroundColor: bool + fCanvas: Canvas + fOnDispose: ControlDisposeProc + fOnDraw: DrawProc + fOnMouseButtonDown: MouseButtonProc + fOnMouseButtonUp: MouseButtonProc + fOnClick: ClickProc + # fOnMouseMove: MouseMoveProc + fOnKeyDown: ControlKeyProc + fOnTextChange: TextChangeProc + tag*: string + + # Drawing: + + Canvas* = ref object of RootObj + fWidth: int + fHeight: int + fFontFamily: string + fFontSize: int + fTextColor: Color + fLineColor: Color + fAreaColor: Color + + Image* = ref object of RootObj + fCanvas: Canvas + + # Window events: + + WindowDisposeEvent* = ref object + window*: Window + cancel*: bool + WindowDisposeProc* = proc(event: WindowDisposeEvent) + + ResizeEvent* = ref object + window*: Window + ResizeProc* = proc(event: ResizeEvent) + + DropFilesEvent* = ref object + window*: Window + files*: seq[string] + DropFilesProc* = proc(event: DropFilesEvent) + + WindowKeyEvent* = ref object + window*: Window + key*: Key + unicode*: int + character*: string # UTF-8 character + WindowKeyProc* = proc(event: WindowKeyEvent) + + # Control events: + + ControlDisposeEvent* = ref object + control*: Control + cancel*: bool + ControlDisposeProc* = proc(event: ControlDisposeEvent) + + DrawEvent* = ref object + control*: Control + DrawProc* = proc(event: DrawEvent) + + MouseButtonEvent* = ref object + control*: Control + button*: MouseButton + x*: int + y*: int + MouseButtonProc* = proc(event: MouseButtonEvent) + + ClickEvent* = ref object + control*: Control + ClickProc* = proc(event: ClickEvent) + + ControlKeyEvent* = ref object + control*: Control + key*: Key + unicode*: int + character*: string # UTF-8 character + cancel*: bool + ControlKeyProc* = proc(event: ControlKeyEvent) + + TextChangeEvent* = ref object + control*: Control + TextChangeProc* = proc(event: TextChangeEvent) + + # Other events: + + ErrorHandlerProc* = proc() + + TimerEvent* = ref object + timer*: Timer + data*: pointer + TimerProc* = proc(event: TimerEvent) + +# Platform-specific extension of Window and Control: +when useWindows(): include "nigui/private/windows/platform_types1" +when useGtk(): include "nigui/private/gtk3/platform_types1" + + +# ---------------------------------------------------------------------------------------- +# Widget Types 2/3 +# ---------------------------------------------------------------------------------------- + +type + # Basic controls: + + Container* = ref object of ControlImpl + fFrame: Frame + fChildControls: seq[Control] + + Frame* = ref object of ControlImpl + fText: string + + Button* = ref object of ControlImpl + fText: string + + Label* = ref object of ControlImpl + fText: string + + TextBox* = ref object of ControlImpl + + TextArea* = ref object of ControlImpl + fWrap: bool + + +# Platform-specific extension of basic controls: +when useWindows(): include "nigui/private/windows/platform_types2" +when useGtk(): include "nigui/private/gtk3/platform_types2" + + +# ---------------------------------------------------------------------------------------- +# Widget Types 3/3 +# ---------------------------------------------------------------------------------------- + +type + LayoutContainer* = ref object of ContainerImpl + fLayout: Layout + fXAlign: XAlign + fYAlign: YAlign + fPadding: int + fSpacing: int + + +# ---------------------------------------------------------------------------------------- +# Global Variables +# ---------------------------------------------------------------------------------------- + +var quitOnLastWindowClose* = true +var clickMaxXYMove* = 20 + +# dummy type and object, needed to use get/set properties +type App = object +var app*: App + + +# ---------------------------------------------------------------------------------------- +# Global/App Procedures +# ---------------------------------------------------------------------------------------- + +proc init*(app: App) + +proc run*(app: App) + +proc quit*(app: App) + +proc processEvents*(app: App) + +proc sleep*(app: App, milliSeconds: float) + +proc errorHandler*(app: App): ErrorHandlerProc +proc `errorHandler=`*(app: App, errorHandler: ErrorHandlerProc) + +proc defaultBackgroundColor*(app: App): Color +proc `defaultBackgroundColor=`*(app: App, color: Color) + +proc defaultTextColor*(app: App): Color +proc `defaultTextColor=`*(app: App, color: Color) + +proc defaultFontFamily*(app: App): string +proc `defaultFontFamily=`*(app: App, fontFamily: string) + +proc defaultFontSize*(app: App): int +proc `defaultFontSize=`*(app: App, fontSize: int) + +proc rgb*(red, green, blue: byte, alpha: byte = 255): Color + + +# ---------------------------------------------------------------------------------------- +# Dialogs +# ---------------------------------------------------------------------------------------- + +proc alert*(window: Window, message: string, title = "Message") {.discardable.} + +type OpenFileDialog* = ref object + title*: string + directory*: string + multiple*: bool + files*: seq[string] + +proc newOpenFileDialog*(): OpenFileDialog + +method run*(dialog: OpenFileDialog) + +type SaveFileDialog* = ref object + title*: string + directory*: string + defaultExtension*: string + defaultName*: string + file*: string + +proc newSaveFileDialog*(): SaveFileDialog + +method run*(dialog: SaveFileDialog) + + +# ---------------------------------------------------------------------------------------- +# Timers +# ---------------------------------------------------------------------------------------- + +proc startTimer*(milliSeconds: int, timerProc: TimerProc, data: pointer = nil): Timer {.discardable.} + +proc startRepeatingTimer*(milliSeconds: int, timerProc: TimerProc, data: pointer = nil): Timer {.discardable.} + +proc stop*(timer: var Timer) + + +# ---------------------------------------------------------------------------------------- +# Canvas +# ---------------------------------------------------------------------------------------- + +proc newCanvas*(control: Control = nil): CanvasImpl + +method destroy*(canvas: Canvas) + +method width*(canvas: Canvas): int + +method height*(canvas: Canvas): int + +method fontFamily*(canvas: Canvas): string +method `fontFamily=`*(canvas: Canvas, fontFamily: string) + +method fontSize*(canvas: Canvas): int +method `fontSize=`*(canvas: Canvas, fontSize: int) + +method textColor*(canvas: Canvas): Color +method `textColor=`*(canvas: Canvas, color: Color) + +method lineColor*(canvas: Canvas): Color +method `lineColor=`*(canvas: Canvas, color: Color) + +method areaColor*(canvas: Canvas): Color +method `areaColor=`*(canvas: Canvas, color: Color) + +method drawText*(canvas: Canvas, text: string, x, y = 0) + +method drawTextCentered*(canvas: Canvas, text: string, x, y = 0, width, height = -1) + +method drawLine*(canvas: Canvas, x1, y1, x2, y2: int) + +method drawRectArea*(canvas: Canvas, x, y, width, height: int) + +method drawRectOutline*(canvas: Canvas, x, y, width, height: int) + +method fill*(canvas: Canvas) + +method drawImage*(canvas: Canvas, image: Image, x, y = 0, width, height = -1) + +method setPixel*(canvas: Canvas, x, y: int, color: Color) + +method getTextLineWidth*(canvas: Canvas, text: string): int + +method getTextLineHeight*(canvas: Canvas): int + +method getTextWidth*(canvas: Canvas, text: string): int + + +# ---------------------------------------------------------------------------------------- +# Image +# ---------------------------------------------------------------------------------------- + +proc newImage*(): Image + +method resize*(image: Image, width, height: int) + +method loadFromFile*(image: Image, filePath: string) + +method saveToPngFile*(image: Image, filePath: string) + +method saveToJpegFile*(image: Image, filePath: string, quality = 80) + +method width*(image: Image): int + +method height*(image: Image): int + +method canvas*(image: Image): Canvas + + +# ---------------------------------------------------------------------------------------- +# Window +# ---------------------------------------------------------------------------------------- + +proc newWindow*(title: string = nil): Window +## Constructor for a Window object. +## If the title is nil, it will be set to the application filename. + +proc init*(window: WindowImpl) +## Initialize a WindowImpl object +## Only needed for own constructors. + +proc dispose*(window: var Window) +proc dispose*(window: Window) + +proc disposed*(window: Window): bool + +method visible*(window: Window): bool +method `visible=`*(window: Window, visible: bool) + +method show*(window: Window) + +method showModal*(window: Window, parent: Window) + +method hide*(window: Window) + +method control*(window: Window): Control +method `control=`*(window: Window, control: Control) + +method add*(window: Window, control: Control) + +method title*(window: Window): string +method `title=`*(window: Window, title: string) + +method x*(window: Window): int +method `x=`*(window: Window, x: int) + +method y*(window: Window): int +method `y=`*(window: Window, y: int) + +method centerOnScreen*(window: Window) + +method width*(window: Window): int +method `width=`*(window: Window, width: int) + +method height*(window: Window): int +method `height=`*(window: Window, height: int) + +method clientWidth*(window: Window): int + +method clientHeight*(window: Window): int + +method iconPath*(window: Window): string +method `iconPath=`*(window: Window, iconPath: string) + +method handleDisposeEvent*(window: Window, event: WindowDisposeEvent) + +method handleResizeEvent*(window: Window, event: ResizeEvent) + +method handleKeyDownEvent*(window: Window, event: WindowKeyEvent) + +method handleDropFilesEvent*(window: Window, event: DropFilesEvent) + +method onDispose*(window: Window): WindowDisposeProc +method `onDispose=`*(window: Window, callback: WindowDisposeProc) + +method onResize*(window: Window): ResizeProc +method `onResize=`*(window: Window, callback: ResizeProc) + +method onDropFiles*(window: Window): DropFilesProc +method `onDropFiles=`*(window: Window, callback: DropFilesProc) + +method onKeyDown*(window: Window): WindowKeyProc +method `onKeyDown=`*(window: Window, callback: WindowKeyProc) + + +# ---------------------------------------------------------------------------------------- +# Control +# ---------------------------------------------------------------------------------------- + +proc newControl*(): Control + +proc init*(control: Control) +proc init*(control: ControlImpl) + +proc dispose*(control: var Control) +proc dispose*(control: Control) + +proc disposed*(control: Control): bool + +method visible*(control: Control): bool +method `visible=`*(control: Control, visible: bool) +method show*(control: Control) +method hide*(control: Control) + +# Allow the outside to walk over child widgets +method childControls*(control: Control): seq[Control] + +method parentControl*(control: Control): Control + +method parentWindow*(control: Control): WindowImpl + +method width*(control: Control): int +# Set the control's width to a fixed value (sets widthMode to fixed) +method `width=`*(control: Control, width: int) + +method height*(control: Control): int +# Set the control's height to a fixed value (sets heightMode to fixed) +method `height=`*(control: Control, height: int) + +method minWidth*(control: Control): int +method `minWidth=`*(control: Control, minWidth: int) + +method minHeight*(control: Control): int +method `minHeight=`*(control: Control, minHeight: int) + +method maxWidth*(control: Control): int +method `maxWidth=`*(control: Control, maxWidth: int) + +method maxHeight*(control: Control): int +method `maxHeight=`*(control: Control, maxHeight: int) + +# Set the control's width and height without changing widthMode or heightMode +method setSize*(control: Control, width, height: int) + +method x*(control: Control): int +method `x=`*(control: Control, x: int) + +method y*(control: Control): int +method `y=`*(control: Control, y: int) + +method setPosition*(control: Control, x, y: int) + +method naturalWidth*(control: Control): int + +method naturalHeight*(control: Control): int + +method wantedWidth*(control: Control): int + +method wantedHeight*(control: Control): int + +method focus*(control: Control) + +method getTextLineWidth*(control: Control, text: string): int + +method getTextLineHeight*(control: Control): int + +method getTextWidth*(control: Control, text: string): int + +method `widthMode=`*(control: Control, mode: WidthMode) +method widthMode*(control: Control): WidthMode + +method heightMode*(control: Control): HeightMode +method `heightMode=`*(control: Control, mode: HeightMode) + +method visibleWidth*(control: Control): int + +method visibleHeight*(control: Control): int + +method xScrollPos*(control: Control): int +method `xScrollPos=`*(control: Control, xScrollPos: int) + +method yScrollPos*(control: Control): int +method `yScrollPos=`*(control: Control, yScrollPos: int) + +method scrollableWidth*(control: Control): int +method `scrollableWidth=`*(control: Control, scrollableWidth: int) + +method scrollableHeight*(control: Control): int +method `scrollableHeight=`*(control: Control, scrollableHeight: int) + +method fontFamily*(control: Control): string +method `fontFamily=`*(control: Control, fontFamily: string) +method setFontFamily*(control: Control, fontFamily: string) +method resetFontFamily*(control: Control) + +method fontSize*(control: Control): int +method `fontSize=`*(control: Control, fontSize: int) +method setFontSize*(control: Control, fontSize: int) +method resetFontSize*(control: Control) + +method backgroundColor*(control: Control): Color +method `backgroundColor=`*(control: Control, color: Color) +method setBackgroundColor*(control: Control, color: Color) +method resetBackgroundColor*(control: Control) + +method textColor*(control: Control): Color +method `textColor=`*(control: Control, color: Color) +method setTextColor*(control: Control, color: Color) +method resetTextColor*(control: Control) + +method forceRedraw*(control: Control) + +method canvas*(control: Control): Canvas + +method handleDisposeEvent*(control: Control, event: ControlDisposeEvent) + +method handleDrawEvent*(control: Control, event: DrawEvent) + +method handleMouseButtonDownEvent*(control: Control, event: MouseButtonEvent) + +method handleMouseButtonUpEvent*(control: Control, event: MouseButtonEvent) + +method handleClickEvent*(control: Control, event: ClickEvent) + +method handleKeyDownEvent*(control: Control, event: ControlKeyEvent) + +method handleTextChangeEvent*(control: Control, event: TextChangeEvent) + +method onDispose*(control: Control): ControlDisposeProc +method `onDispose=`*(control: Control, callback: ControlDisposeProc) + +method onDraw*(control: Control): DrawProc +method `onDraw=`*(control: Control, callback: DrawProc) + +method onMouseButtonDown*(control: Control): MouseButtonProc +method `onMouseButtonDown=`*(control: Control, callback: MouseButtonProc) + +method onMouseButtonUp*(control: Control): MouseButtonProc +method `onMouseButtonUp=`*(control: Control, callback: MouseButtonProc) + +method onClick*(control: Control): ClickProc +method `onClick=`*(control: Control, callback: ClickProc) + +method onKeyDown*(control: Control): ControlKeyProc +method `onKeyDown=`*(control: Control, callback: ControlKeyProc) + +method onTextChange*(control: Control): TextChangeProc +method `onTextChange=`*(control: Control, callback: TextChangeProc) + + +# ---------------------------------------------------------------------------------------- +# Container +# ---------------------------------------------------------------------------------------- + +proc newContainer*(): Container + +proc init*(container: Container) +proc init*(container: ContainerImpl) + +method frame*(container: Container): Frame +method `frame=`*(container: Container, frame: Frame) + +method add*(container: Container, control: Control) +method remove*(container: Container, control: Control) + +method getPadding*(container: Container): Spacing + +method setInnerSize*(container: Container, width, height: int) + + +# ---------------------------------------------------------------------------------------- +# LayoutContainer +# ---------------------------------------------------------------------------------------- + +proc newLayoutContainer*(layout: Layout): LayoutContainer + +method layout*(container: LayoutContainer): Layout +method `layout=`*(container: LayoutContainer, layout: Layout) + +method xAlign*(container: LayoutContainer): XAlign +method `xAlign=`*(container: LayoutContainer, xAlign: XAlign) + +method yAlign*(container: LayoutContainer): YAlign +method `yAlign=`*(container: LayoutContainer, yAlign: YAlign) + +method padding*(container: LayoutContainer): int +method `padding=`*(container: LayoutContainer, padding: int) + +method spacing*(container: LayoutContainer): int +method `spacing=`*(container: LayoutContainer, spacing: int) + + +# ---------------------------------------------------------------------------------------- +# Frame +# ---------------------------------------------------------------------------------------- + +proc newFrame*(text = ""): Frame + +proc init*(frame: Frame) +proc init*(frame: NativeFrame) + +method text*(frame: Frame): string +method `text=`*(frame: Frame, text: string) + +method getPadding*(frame: Frame): Spacing + + +# ---------------------------------------------------------------------------------------- +# Button +# ---------------------------------------------------------------------------------------- + +proc newButton*(text = ""): Button + +proc init*(button: Button) +proc init*(button: NativeButton) + +method text*(button: Button): string +method `text=`*(button: Button, text: string) + + +# ---------------------------------------------------------------------------------------- +# Label +# ---------------------------------------------------------------------------------------- + +proc newLabel*(text = ""): Label + +proc init*(label: Label) +proc init*(label: NativeLabel) + +method text*(label: Label): string +method `text=`*(label: Label, text: string) + + +# ---------------------------------------------------------------------------------------- +# TextBox +# ---------------------------------------------------------------------------------------- + +proc newTextBox*(text = ""): TextBox + +proc init*(textBox: TextBox) +proc init*(textBox: NativeTextBox) + +method text*(textBox: TextBox): string +method `text=`*(textBox: TextBox, text: string) + + +# ---------------------------------------------------------------------------------------- +# TextArea +# ---------------------------------------------------------------------------------------- + +proc newTextArea*(text = ""): TextArea + +proc init*(textArea: TextArea) +proc init*(textArea: NativeTextArea) + +method text*(textArea: TextArea): string +method `text=`*(textArea: TextArea, text: string) +method addText*(textArea: TextArea, text: string) +method addLine*(textArea: TextArea, text = "") + +method scrollToBottom*(textArea: TextArea) + +method wrap*(textArea: TextArea): bool +method `wrap=`*(textArea: TextArea, wrap: bool) + + +# ---------------------------------------------------------------------------------------- +# Private Procedures Predeclaration +# ---------------------------------------------------------------------------------------- + +proc raiseError(msg: string, showAlert = true, title = "NiGui Error") + +proc handleException() + +proc runMainLoop() + +proc init(window: Window) + +method destroy(window: Window) + +proc triggerRelayout(window: Window) + +method destroy(control: Control) + +proc triggerRelayout(control: Control) + +proc triggerRelayoutIfModeIsAuto(control: Control) + +method relayout(control: Control, availableWidth, availableHeight: int) + +method realignChildControls(control: Control) + +method setControlPosition(container: Container, control: Control, x, y: int) + +proc countLines(s: string): int + + +# ======================================================================================== +# +# Implementation +# +# ======================================================================================== + +import math +import os +import strutils +import times + + +# ---------------------------------------------------------------------------------------- +# Global Variables +# ---------------------------------------------------------------------------------------- + +var fErrorHandler: ErrorHandlerProc = nil +var windowList: seq[Window] = @[] +var fScrollbarSize = -1 + +# Default style: +var fDefaultBackgroundColor: Color # initialized by platform-specific init() +var fDefaultTextColor: Color # initialized by platform-specific init() +var fDefaultFontFamily = "" +var fDefaultFontSize = 15 + + +# ---------------------------------------------------------------------------------------- +# Global/App Procedures +# ---------------------------------------------------------------------------------------- + +proc raiseError(msg: string, showAlert = true, title = "NiGui Error") = + if showAlert: + alert(nil, msg & "\n\n" & getStackTrace(), title) + raise newException(Exception, msg) + +proc handleException() = + if fErrorHandler == nil: + raiseError(getCurrentExceptionMsg(), true, "Unhandled Exception") + else: + fErrorHandler() + +proc rgb(red, green, blue: byte, alpha: byte = 255): Color = + result.red = red + result.green = green + result.blue = blue + result.alpha = alpha + +proc countLines(s: string): int = strutils.countLines(s) + 1 + +proc sleep(app: App, milliSeconds: float) = + let t = epochTime() + milliSeconds / 1000 + while epochTime() < t: + app.processEvents() + os.sleep(20) + +proc run(app: App) = + while true: + try: + runMainLoop() + break + except: + handleException() + +proc quit(app: App) = quit() + +proc errorHandler(app: App): ErrorHandlerProc = fErrorHandler + +proc `errorHandler=`(app: App, errorHandler: ErrorHandlerProc) = fErrorHandler = errorHandler + +proc defaultBackgroundColor(app: App): Color = fDefaultBackgroundColor + +proc updateBackgroundColor(control: Control) = + if control.fUseDefaultBackgroundColor and control.backgroundColor != fDefaultBackgroundColor: + control.setBackgroundColor(fDefaultBackgroundColor) + for child in control.childControls: + child.updateBackgroundColor() + +proc `defaultBackgroundColor=`(app: App, color: Color) = + fDefaultBackgroundColor = color + for window in windowList: + let control = window.control + if control != nil: + control.updateBackgroundColor() + +proc defaultTextColor(app: App): Color = fDefaultTextColor + +proc updateTextColor(control: Control) = + if control.fUseDefaultTextColor and control.textColor != fDefaultTextColor: + control.setTextColor(fDefaultTextColor) + for child in control.childControls: + child.updateTextColor() + +proc `defaultTextColor=`(app: App, color: Color) = + fDefaultTextColor = color + for window in windowList: + let control = window.control + if control != nil: + control.updateTextColor() + +proc defaultFontFamily(app: App): string = fDefaultFontFamily + +proc updateFontFamily(control: Control) = + if control.fUseDefaultFontFamily and control.fontFamily != fDefaultFontFamily: + control.setFontFamily(fDefaultFontFamily) + for child in control.childControls: + child.updateFontFamily() + +proc `defaultFontFamily=`(app: App, fontFamily: string) = + fDefaultFontFamily = fontFamily + for window in windowList: + let control = window.control + if control != nil: + control.updateFontFamily() + +proc defaultFontSize(app: App): int = fDefaultFontSize + +proc updateFontSize(control: Control) = + if control.fUseDefaultFontSize and control.fontSize != fDefaultFontSize: + control.setFontSize(fDefaultFontSize) + for child in control.childControls: + child.updateFontSize() + +proc `defaultFontSize=`(app: App, fontSize: int) = + fDefaultFontSize = fontSize + for window in windowList: + let control = window.control + if control != nil: + control.updateFontSize() + +proc newOpenFileDialog(): OpenFileDialog = + result = new OpenFileDialog + result.title = "Open File" + result.directory = getCurrentDir() + result.files = @[] + +proc newSaveFileDialog(): SaveFileDialog = + result = new SaveFileDialog + result.title = "Save File" + result.directory = getCurrentDir() + result.defaultExtension = "" + result.defaultName = "" + result.file = "" + + +# ---------------------------------------------------------------------------------------- +# Canvas +# ---------------------------------------------------------------------------------------- + +proc newCanvas(control: Control = nil): CanvasImpl = + result = new CanvasImpl + result.fLineColor = rgb(0, 0, 0) + result.fAreaColor = rgb(0, 0, 0) + if control == nil: + result.fFontFamily = app.defaultFontFamily + result.fFontSize = app.defaultFontSize + result.fTextColor = app.defaultTextColor + else: + result.fFontFamily = control.fontFamily + result.fFontSize = control.fontSize + result.fTextColor = control.textColor + result.fWidth = control.width + result.fHeight = control.height + control.fCanvas = result + +method destroy(canvas: Canvas) = discard + +method width(canvas: Canvas): int = canvas.fWidth + +method height(canvas: Canvas): int= canvas.fHeight + +method fontFamily(canvas: Canvas): string = canvas.fFontFamily + +method `fontFamily=`(canvas: Canvas, fontFamily: string) = canvas.fFontFamily = fontFamily + +method fontSize(canvas: Canvas): int = canvas.fFontSize + +method `fontSize=`(canvas: Canvas, fontSize: int) = canvas.fFontSize = fontSize + +method textColor(canvas: Canvas): Color = canvas.fTextColor + +method `textColor=`(canvas: Canvas, color: Color) = canvas.fTextColor = color + +method lineColor(canvas: Canvas): Color = canvas.fLineColor + +method `lineColor=`(canvas: Canvas, color: Color) = canvas.fLineColor = color + +method areaColor(canvas: Canvas): Color = canvas.fAreaColor + +method `areaColor=`(canvas: Canvas, color: Color) = canvas.fAreaColor = color + +method getTextLineWidth(canvas: Canvas, text: string): int = + result = text.len * 7 + # should be overrriden by CanvasImpl + +method getTextLineHeight(canvas: Canvas): int = + result = 20 + # should be overrriden by CanvasImpl + +method getTextWidth(canvas: Canvas, text: string): int = + result = 0 + for line in text.splitLines: + result = max(result, canvas.getTextLineWidth(line)) + +method drawTextCentered(canvas: Canvas, text: string, x, y = 0, width, height = -1) = + var w = width + if w == -1: + w = canvas.width + var h = height + if h == -1: + h = canvas.height + let rx = x + (w - canvas.getTextWidth(text)) div 2 + let ry = y + (h - canvas.getTextLineHeight()) div 2 + canvas.drawText(text, rx, ry) + +method fill(canvas: Canvas) = canvas.drawRectArea(0, 0, canvas.width, canvas.height) + + +# ---------------------------------------------------------------------------------------- +# Image +# ---------------------------------------------------------------------------------------- + +proc newImage(): Image = + result = new ImageImpl + result.fCanvas = newCanvas() + +method width(image: Image): int = image.canvas.width + +method height(image: Image): int = image.canvas.height + +method canvas(image: Image): Canvas = image.fCanvas + + +# ---------------------------------------------------------------------------------------- +# Window +# ---------------------------------------------------------------------------------------- + +proc newWindow(title: string = nil): Window = + result = new WindowImpl + result.WindowImpl.init() + if title != nil: + result.title = title + + +proc init(window: Window) = + window.fVisible = false + window.fWidth = 640 # do not trigger resize + window.height = 480 # trigger resize + window.fX = -1 # window will be centered on screen + window.fY = -1 + window.title = getAppFilename().extractFilename().changeFileExt("") + var defaultIconPath = getAppFilename().changeFileExt("") & ".png" + if defaultIconPath.fileExists(): + window.iconPath = defaultIconPath + windowList.add(window) + window.triggerRelayout() + + +method destroy(window: Window) = + if window.fControl != nil: + window.fControl.destroy() + # should be extended by WindowImpl + +proc disposeInner(window: Window): bool = + var event = new WindowDisposeEvent + event.window = window + window.handleDisposeEvent(event) + if event.cancel: + return false + window.destroy() + let i = windowList.find(window) + windowList.delete(i) + if quitOnLastWindowClose and windowList.len == 0: + quit() + window.fDisposed = true + return true + +proc dispose(window: var Window) = + if window.disposeInner(): + window = nil + +proc dispose(window: Window) = + discard window.disposeInner() + +proc disposed(window: Window): bool = window == nil or window.fDisposed + +method title(window: Window): string = window.fTitle + +method `title=`(window: Window, title: string) = window.fTitle = title + +method control(window: Window): Control = window.fControl + +method `control=`(window: Window, control: Control) = + window.fControl = control + control.fParentWindow = window + # should be extended by WindowImpl + +method add(window: Window, control: Control) = + if window.control != nil: + raiseError("Window can have only one control.") + window.control = control + +method visible(window: Window): bool = window.fVisible + +method `visible=`(window: Window, visible: bool) = + window.fVisible = visible + if window.x == -1 or window.y == -1: + window.centerOnScreen() + +method show(window: Window) = window.visible = true + +method showModal(window: Window, parent: Window) = + window.visible = true + # should be extended by WindowImpl + +method hide(window: Window) = window.visible = false + +method x(window: Window): int = window.fX + +method `x=`(window: Window, x: int) = + window.fX = x + # should be extended by WindowImpl + +method y(window: Window): int = window.fY + +method `y=`(window: Window, y: int) = + window.fY = y + # should be extended by WindowImpl + +method centerOnScreen(window: Window) = + discard # has to be implemented in WindowImpl + +method width(window: Window): int = window.fWidth + +method height(window: Window): int = window.fHeight + +method `width=`(window: Window, width: int) = + window.fWidth = width + window.triggerRelayout() + var event = new ResizeEvent + event.window = window + window.handleResizeEvent(event) + +method `height=`(window: Window, height: int) = + window.fHeight = height + window.triggerRelayout() + var event = new ResizeEvent + event.window = window + window.handleResizeEvent(event) + +method clientWidth(window: Window): int = window.fClientWidth + +method clientHeight(window: Window): int = window.fClientHeight + +proc triggerRelayout(window: Window) = + if window.control == nil: + return + # echo "" + # echo "WindowImpl:triggerRelayout()" + # echo "window size: " & $window.clientWidth & ", " & $window.clientHeight + window.control.relayout(window.clientWidth, window.clientHeight) + +method iconPath(window: Window): string = window.fIconPath + +method `iconPath=`(window: Window, iconPath: string) = + window.fIconPath = iconPath + # should be extended by WindowImpl + +method handleDisposeEvent(window: Window, event: WindowDisposeEvent) = + # can be overriden by custom window + let callback = window.onDispose + if callback != nil: + callback(event) + +method handleResizeEvent(window: Window, event: ResizeEvent) = + # can be overriden by custom window + let callback = window.onResize + if callback != nil: + callback(event) + +method handleDropFilesEvent(window: Window, event: DropFilesEvent) = + # can be overriden by custom window + let callback = window.onDropFiles + if callback != nil: + callback(event) + +method handleKeyDownEvent(window: Window, event: WindowKeyEvent) = + # can be overriden by custom window + let callback = window.onKeyDown + if callback != nil: + callback(event) + +method onDispose(window: Window): WindowDisposeProc = window.fOnDispose +method `onDispose=`(window: Window, callback: WindowDisposeProc) = window.fOnDispose = callback + +method onResize(window: Window): ResizeProc = window.fOnResize +method `onResize=`(window: Window, callback: ResizeProc) = window.fOnResize = callback + +method onDropFiles(window: Window): DropFilesProc = window.fOnDropFiles +method `onDropFiles=`(window: Window, callback: DropFilesProc) = window.fOnDropFiles = callback + +method onKeyDown(window: Window): WindowKeyProc = window.fOnKeyDown +method `onKeyDown=`(window: Window, callback: WindowKeyProc) = window.fOnKeyDown = callback + + + +# ---------------------------------------------------------------------------------------- +# Control +# ---------------------------------------------------------------------------------------- + +proc newControl(): Control = + result = new ControlImpl + result.ControlImpl.init() + +proc init(control: Control) = + control.tag = "" + control.fWidthMode = WidthMode_Expand + control.fHeightMode = HeightMode_Expand + control.fScrollableWidth = -1 + control.fScrollableHeight = -1 + control.resetFontFamily() + control.resetFontSize() + control.resetTextColor() + control.resetBackgroundColor() + control.show() + # should be extended by WindowImpl + +method destroy(control: Control) = + discard # nothing to do here + # should be extended by WindowImpl + +proc dispose(control: var Control) = + control.destroy() + control = nil + +proc dispose(control: Control) = + control.destroy() + control.fDisposed = true + +proc disposed(control: Control): bool = control == nil or control.fDisposed + +method visible(control: Control): bool = control.fVisible + +method `visible=`(control: Control, visible: bool) = + control.fVisible = visible + control.triggerRelayout() + # should be extended by WindowImpl + +method show(control: Control) = control.visible = true + +method hide(control: Control) = control.visible = false + +method width(control: Control): int = control.fWidth + +method height(control: Control): int = control.fHeight + +method `width=`(control: Control, width: int) = + control.setSize(width, control.fHeight) + control.widthMode = WidthMode_Static + +method `height=`(control: Control, height: int) = + control.setSize(control.fWidth, height) + control.heightMode = HeightMode_Static + +method minWidth(control: Control): int = control.fMinWidth + +method `minWidth=`(control: Control, minWidth: int) = + control.fMinWidth = minWidth + control.triggerRelayout() + +method minHeight(control: Control): int = control.fMinHeight + +method `minHeight=`(control: Control, minHeight: int) = + control.fMinHeight = minHeight + control.triggerRelayout() + +method maxWidth(control: Control): int = control.fMaxWidth + +method `maxWidth=`(control: Control, maxWidth: int) = + control.fMaxWidth = maxWidth + control.triggerRelayout() + +method maxHeight(control: Control): int = control.fMaxHeight + +method `maxHeight=`(control: Control, maxHeight: int) = + control.fMaxHeight = maxHeight + control.triggerRelayout() + +method setSize(control: Control, width, height: int) = + control.fWidth = width + control.fHeight = height + if control.canvas != nil: + control.canvas.fWidth = width + control.canvas.fHeight = height + control.realignChildControls() + # should be extended by ControlImpl + +method setSize(control: ControlImpl, width, height: int) # required pre declaration + +method `x=`(control: Control, x: int) = + if control.fParentControl == nil: + raiseError("Control cannot be moved, when it is not inside a container.") + cast[Container](control.fParentControl).setControlPosition(control, x, control.y) + +method x(control: Control): int = control.fX + +method `y=`(control: Control, y: int) = + if control.fParentControl == nil: + raiseError("Control cannot be moved, when it is not inside a container.") + cast[Container](control.fParentControl).setControlPosition(control, control.x, y) + +method y(control: Control): int = control.fY + +method setPosition(control: Control, x, y: int) = + control.fX = x + control.fY = y + # should be extended by ControlImpl + +method naturalWidth(control: Control): int = control.width + +method naturalHeight(control: Control): int = control.height + +method wantedWidth(control: Control): int = + if control.widthMode == WidthMode_Static: + result = control.width + else: + result = control.naturalWidth + if result != -1 and control.minWidth > result: + result = control.minWidth + if control.maxWidth > 0 and control.maxWidth < result: + result = control.maxWidth + +method wantedHeight(control: Control): int = + if control.heightMode == HeightMode_Static: + result = control.height + else: + result = control.naturalHeight + if result != -1 and control.minHeight > result: + result = control.minHeight + if control.maxHeight > 0 and control.maxHeight < result: + result = control.maxHeight + +method `widthMode=`(control: Control, mode: WidthMode) = + control.fWidthMode = mode + control.triggerRelayout() + +method `heightMode=`(control: Control, mode: HeightMode) = + control.fHeightMode = mode + control.triggerRelayout() + +method widthMode(control: Control): WidthMode = control.fWidthMode + +method heightMode(control: Control): HeightMode = control.fHeightMode + +method childControls(control: Control): seq[Control] = @[] + +method parentControl(control: Control): Control = control.fParentControl + +method parentWindow(control: Control): WindowImpl = + if control.fParentControl == nil: + result = cast[WindowImpl](control.fParentWindow) + else: + result = control.parentControl.parentWindow + +proc triggerRelayout(control: Control) = + var con = control + while con.parentControl != nil: + con = con.parentControl + if con.parentWindow != nil: + con.parentWindow.triggerRelayout() + if control.parentControl != nil: + control.parentControl.realignChildControls() + control.realignChildControls() + +proc triggerRelayoutIfModeIsAuto(control: Control) = + if control.widthMode == WidthMode_Auto or control.heightMode == HeightMode_Auto: + control.triggerRelayout() + +method relayout(control: Control, availableWidth, availableHeight: int) = + # echo "" + # echo control.tag & ".relayout(): " + # echo " available size: " & $availableWidth & ", " & $availableHeight + var width = control.width + var height = control.height + var sizeChanged = false + if control.widthMode == WidthMode_Auto: + let naturalWidth = control.naturalWidth + # echo " naturalWidth: " & $naturalWidth + if naturalWidth == -1: + width = availableWidth + else: + width = min(availableWidth, naturalWidth) + sizeChanged = true + elif control.widthMode in {WidthMode_Expand, WidthMode_Fill}: + width = availableWidth + sizeChanged = true + if control.heightMode == HeightMode_Auto: + let naturalHeight = control.naturalHeight + # echo " naturalHeight: " & $naturalHeight + if naturalHeight == -1: + height = availableHeight + else: + height = min(availableHeight, naturalHeight) + sizeChanged = true + elif control.heightMode in {HeightMode_Expand, HeightMode_Fill}: + height = availableHeight + sizeChanged = true + if sizeChanged: + # echo " new size: " & $width & ", " & $height + control.setSize(width, height) + +method realignChildControls(control: Control) = discard + +method focus(control: Control) = + discard + # should be overrriden by ControlImpl + +method getTextLineWidth(control: Control, text: string): int = + result = text.len * 7 + # should be overrriden by ControlImpl + +method getTextLineHeight(control: Control): int = + result = 20 + # should be overrriden by ControlImpl + +method getTextWidth(control: Control, text: string): int = + result = 0 + for line in text.splitLines: + result = max(result, control.getTextLineWidth(line)) + +method visibleWidth(control: Control): int = + result = control.width + if control.fXScrollEnabled: + result.dec(fScrollbarSize) + +method visibleHeight(control: Control): int = + result = control.height + if control.fYScrollEnabled: + result.dec(fScrollbarSize) + +method xScrollPos(control: Control): int = control.fXScrollPos + +method `xScrollPos=`(control: Control, xScrollPos: int) = + control.fXScrollPos = xScrollPos + +method yScrollPos(control: Control): int = control.fYScrollPos + +method `yScrollPos=`(control: Control, yScrollPos: int) = + control.fYScrollPos = yScrollPos + +method scrollableWidth(control: Control): int = control.fScrollableWidth + +method `scrollableWidth=`(control: Control, scrollableWidth: int) = + control.fScrollableWidth = scrollableWidth + +method scrollableHeight(control: Control): int = control.fScrollableHeight + +method `scrollableHeight=`(control: Control, scrollableHeight: int) = + control.fScrollableHeight = scrollableHeight + +method fontFamily(control: Control): string = control.fFontFamily + +method `fontFamily=`(control: Control, fontFamily: string) = + control.setFontFamily(fontFamily) + control.fUseDefaultFontFamily = false + +method setFontFamily(control: Control, fontFamily: string) = + control.fFontFamily = fontFamily + control.triggerRelayoutIfModeIsAuto() + # should be extended by ControlImpl + +method resetFontFamily(control: Control) = + control.setFontFamily(fDefaultFontFamily) + control.fUseDefaultFontFamily = true + +method fontSize(control: Control): int = control.fFontSize + +method `fontSize=`(control: Control, fontSize: int) = + control.setFontSize(fontSize) + control.fUseDefaultFontSize = false + +method setFontSize(control: Control, fontSize: int) = + control.fFontSize = fontSize + control.triggerRelayoutIfModeIsAuto() + # should be extended by ControlImpl + +method resetFontSize(control: Control) = + control.setFontSize(fDefaultFontSize) + control.fUseDefaultFontSize = true + +method backgroundColor(control: Control): Color = control.fBackgroundColor + +method `backgroundColor=`(control: Control, color: Color) = + control.setBackgroundColor(color) + control.fUseDefaultBackgroundColor = false + +method setBackgroundColor(control: Control, color: Color) = + control.fBackgroundColor = color + control.forceRedraw() + # should be extended by ControlImpl + +method resetBackgroundColor(control: Control) = + control.setBackgroundColor(fDefaultBackgroundColor) + control.fUseDefaultBackgroundColor = true + +method textColor(control: Control): Color = control.fTextColor + +method `textColor=`(control: Control, color: Color) = + control.setTextColor(color) + control.fUseDefaultTextColor = false + +method setTextColor(control: Control, color: Color) = + control.fTextColor = color + control.forceRedraw() + # should be extended by ControlImpl + +method resetTextColor*(control: Control) = + control.setTextColor(fDefaultTextColor) + control.fUseDefaultTextColor = true + +method forceRedraw(control: Control) = + discard + # should be implemented by ControlImpl + +method canvas(control: Control): Canvas = control.fCanvas + +method handleDisposeEvent(control: Control, event: ControlDisposeEvent) = + # can be overriden by custom window + let callback = control.onDispose + if callback != nil: + callback(event) + +method handleDrawEvent(control: Control, event: DrawEvent) = + # can be implemented by custom control + let callback = control.onDraw + if callback != nil: + callback(event) + +method handleMouseButtonDownEvent(control: Control, event: MouseButtonEvent) = + # can be implemented by custom control + let callback = control.onMouseButtonDown + if callback != nil: + callback(event) + +method handleMouseButtonUpEvent(control: Control, event: MouseButtonEvent) = + # can be implemented by custom control + let callback = control.onMouseButtonUp + if callback != nil: + callback(event) + +method handleClickEvent(control: Control, event: ClickEvent) = + # can be overridden by custom button + let callback = control.onClick + if callback != nil: + callback(event) + +method handleKeyDownEvent(control: Control, event: ControlKeyEvent) = + # can be implemented by custom control + let callback = control.onKeyDown + if callback != nil: + callback(event) + +method handleTextChangeEvent(control: Control, event: TextChangeEvent) = + # can be implemented by custom control + let callback = control.onTextChange + if callback != nil: + callback(event) + +method onDispose(control: Control): ControlDisposeProc = control.fOnDispose +method `onDispose=`(control: Control, callback: ControlDisposeProc) = control.fOnDispose = callback + +method onDraw(control: Control): DrawProc = control.fOnDraw +method `onDraw=`(control: Control, callback: DrawProc) = control.fOnDraw = callback + +method onMouseButtonDown(control: Control): MouseButtonProc = control.fOnMouseButtonDown +method `onMouseButtonDown=`(control: Control, callback: MouseButtonProc) = control.fOnMouseButtonDown = callback + +method onMouseButtonUp(control: Control): MouseButtonProc = control.fOnMouseButtonUp +method `onMouseButtonUp=`(control: Control, callback: MouseButtonProc) = control.fOnMouseButtonUp = callback + +method onClick(control: Control): ClickProc = control.fOnClick +method `onClick=`(control: Control, callback: ClickProc) = control.fOnClick = callback + +method onKeyDown(control: Control): ControlKeyProc = control.fOnKeyDown +method `onKeyDown=`(control: Control, callback: ControlKeyProc) = control.fOnKeyDown = callback + +method onTextChange(control: Control): TextChangeProc = control.fOnTextChange +method `onTextChange=`(control: Control, callback: TextChangeProc) = control.fOnTextChange = callback + + +# ---------------------------------------------------------------------------------------- +# Container +# ---------------------------------------------------------------------------------------- + +proc newContainer(): Container = + result = new ContainerImpl + result.ContainerImpl.init() + +proc init(container: Container) = + container.fChildControls = @[] + container.ControlImpl.init() + container.fWidthMode = WidthMode_Auto + container.fHeightMode = HeightMode_Auto + +method frame(container: Container): Frame = container.fFrame + +method `frame=`(container: Container, frame: Frame) = + if frame.fParentControl != nil: + raiseError("Frame can be assigned only to one container.") + container.fFrame = frame + if frame != nil: + frame.fParentControl = container + container.tag = frame.tag + container.triggerRelayout() + if container.frame != nil: + container.frame.setSize(container.width, container.height) + # should be extended by NativeFrame + +method setSize(container: Container, width, height: int) = + procCall container.ControlImpl.setSize(width, height) + if container.frame != nil: + container.frame.setSize(width, height) + +method childControls(container: Container): seq[Control] = container.fChildControls + +method add(container: Container, control: Control) = + if control.fParentControl != nil: + raiseError("Control can be added only to one container.") + container.fChildControls.add(control) + control.fParentControl = container + control.fIndex = 0 + container.triggerRelayout() + +method remove(container: Container, control: Control) = + discard + # if container != control.fParentControl: + # raiseError("control can not be removed because it is not member of the container") + # else: + # let startIndex = control.fIndex + # container.childControls.del(control.fIndex) + # for i in startIndex..container.childControls.high: + # container.childControl[i].fIndex = i + # control.parentControl = nil + +method setControlPosition(container: Container, control: Control, x, y: int) = + control.setPosition(x, y) + container.triggerRelayout() + +method minWidth(container: Container): int = + result = 0 + for control in container.childControls: + if not control.visible: + continue + result = max(result, control.x + control.minWidth) + let padding = container.getPadding() + result.inc(padding.left) + result.inc(padding.right) + result = max(result, container.fMinWidth) + +method minHeight(container: Container): int = + result = 0 + for control in container.childControls: + if not control.visible: + continue + result = max(result, control.y + control.minHeight) + let padding = container.getPadding() + result.inc(padding.top) + result.inc(padding.bottom) + result = max(result, container.fMinHeight) + +method naturalWidth(container: Container): int = + if container.widthMode == WidthMode_Static: + return container.width + if container.widthMode == WidthMode_Expand: + return -1 + result = 0 + for control in container.childControls: + if not control.visible: + continue + if control.widthMode == WidthMode_Expand: + return -1 + result = max(result, control.x + control.wantedWidth) + let padding = container.getPadding() + result.inc(padding.left) + result.inc(padding.right) + if container.frame != nil and container.frame.visible: + result = max(result, container.frame.naturalWidth) + +method naturalHeight(container: Container): int = + if container.heightMode == HeightMode_Static: + return container.height + if container.heightMode == HeightMode_Expand: + return -1 + result = 0 + for control in container.childControls: + if not control.visible: + continue + if control.heightMode == HeightMode_Expand: + return -1 + result = max(result, control.y + control.wantedHeight) + let padding = container.getPadding() + result.inc(padding.top) + result.inc(padding.bottom) + +method getPadding(container: Container): Spacing = + if container.frame != nil and container.frame.visible: + result = container.frame.getPadding() + +method setInnerSize(container: Container, width, height: int) = discard + # should be extended by ContainerImpl + +method updateInnerSize(container: Container, pInnerWidth, pInnerHeight: int) {.base.} = + let padding = container.getPadding() + let clientWidth = container.width - padding.left - padding.right + let clientHeight = container.height - padding.top - padding.bottom + var innerWidth = pInnerWidth + var innerHeight = pInnerHeight + + discard """ container.xScrollEnabled = innerWidth > clientWidth + + if container.xScrollEnabled and innerHeight + 20 > clientHeight: + innerHeight.inc(20) # Space for scrollbar + container.yScrollEnabled = true + else: + container.yScrollEnabled = innerHeight > clientHeight + if container.yScrollEnabled and innerWidth + 20 > clientWidth: + innerWidth.inc(20) # Space for scrollbar + container.xScrollEnabled = true """ + + # TODO: rework + + innerWidth = max(innerWidth, clientWidth) + innerHeight = max(innerHeight, clientHeight) + + container.scrollableWidth = innerWidth + container.scrollableHeight = innerHeight + container.setInnerSize(innerWidth, innerHeight) + +method realignChildControls(container: Container) = + let padding = container.getPadding() + var innerWidth = container.wantedWidth + var innerHeight = container.wantedHeight + if innerWidth == -1: + innerWidth = container.width + if innerHeight == -1: + innerHeight = container.height + container.updateInnerSize(innerWidth - padding.left - padding.right, innerHeight - padding.top - padding.bottom) + +method `onDraw=`(container: ContainerImpl, callback: DrawProc) = raiseError("ContainerImpl does not allow onDraw.") + + +# ---------------------------------------------------------------------------------------- +# LayoutContainer +# ---------------------------------------------------------------------------------------- + +proc newLayoutContainer(layout: Layout): LayoutContainer = + result = new LayoutContainer + result.init() + result.layout = layout + result.xAlign = XAlign_Left + result.yAlign = YAlign_Top + result.spacing = 4 + result.padding = 2 + +method naturalWidth(container: LayoutContainer): int = + # echo container.tag & ".naturalWidth" + if container.widthMode == WidthMode_Static: + return container.width + if container.widthMode == WidthMode_Expand: + return -1 + result = 0 + for control in container.childControls: + if not control.visible: + continue + if control.widthMode == WidthMode_Expand or control.wantedWidth == -1: + return -1 + if container.layout == Layout_Horizontal: + result.inc(control.wantedWidth) + else: + result = max(result, control.wantedWidth) + let padding = container.getPadding() + result.inc(padding.left) + result.inc(padding.right) + result.inc(container.padding * 2) + if container.layout == Layout_Horizontal and container.childControls.len > 1: + result.inc(container.spacing * (container.childControls.len - 1)) + if container.frame != nil and container.frame.visible: + result = max(result, container.frame.naturalWidth) + +method naturalHeight(container: LayoutContainer): int = + if container.heightMode == HeightMode_Static: + return container.height + if container.heightMode == HeightMode_Expand: + return -1 + result = 0 + for control in container.childControls: + if not control.visible: + continue + if control.heightMode == HeightMode_Expand or control.wantedHeight == -1: + return -1 + if container.layout == Layout_Vertical: + result.inc(control.wantedHeight) + else: + result = max(result, control.wantedHeight) + let padding = container.getPadding() + result.inc(padding.top) + result.inc(padding.bottom) + result.inc(container.padding * 2) + if container.layout == Layout_Vertical and container.childControls.len > 1: + result.inc(container.spacing * (container.childControls.len - 1)) + +method minWidth(container: LayoutContainer): int = + result = 0 + for control in container.childControls: + if not control.visible: + continue + if container.layout == Layout_Horizontal: + result.inc(control.minWidth) + else: + result = max(result, control.minWidth) + let padding = container.getPadding() + result.inc(padding.left) + result.inc(padding.right) + result.inc(container.padding * 2) + if container.layout == Layout_Horizontal and container.childControls.len > 1: + result.inc(container.spacing * (container.childControls.len - 1)) + result = max(result, container.fMinWidth) + +method minHeight(container: LayoutContainer): int = + result = 0 + for control in container.childControls: + if not control.visible: + continue + if container.layout == Layout_Vertical: + result.inc(control.minHeight) + else: + result = max(result, control.minHeight) + let padding = container.getPadding() + result.inc(padding.top) + result.inc(padding.bottom) + result.inc(container.padding * 2) + if container.layout == Layout_Vertical and container.childControls.len > 1: + result.inc(container.spacing * (container.childControls.len - 1)) + result = max(result, container.fMinHeight) + +method setControlPosition(container: LayoutContainer, control: Control, x, y: int) = + raiseError("Controls inside a LayoutContainer cannot be moved manually.") + +method layout(container: LayoutContainer): Layout = container.fLayout + +method `layout=`(container: LayoutContainer, layout: Layout) = + container.fLayout = layout + container.triggerRelayout() + +method xAlign(container: LayoutContainer): XAlign = container.fXAlign + +method `xAlign=`(container: LayoutContainer, xAlign: XAlign) = + container.fXAlign = xAlign + container.realignChildControls() + +method yAlign(container: LayoutContainer): YAlign = container.fYAlign + +method `yAlign=`(container: LayoutContainer, yAlign: YAlign) = + container.fYAlign = yAlign + container.realignChildControls() + +method padding(container: LayoutContainer): int = container.fPadding + +method `padding=`(container: LayoutContainer, padding: int) = + container.fPadding = padding + container.triggerRelayout() + +method spacing(container: LayoutContainer): int = container.fSpacing + +method `spacing=`(container: LayoutContainer, spacing: int) = + container.fSpacing = spacing + container.triggerRelayout() + +method realignChildControls(container: LayoutContainer) = + # echo "" + # echo container.tag & ".realignChildControls()" + if container.fChildControls.len == 0: + return + # echo " container size: " & $container.width & ", " & $container.height + let padding = container.getPadding() + let clientWidth = container.width - padding.left - padding.right + let clientHeight = container.height - padding.top - padding.bottom + # echo " client size: " & $clientWidth & ", " & $clientHeight + var minInnerWidth = 0 + var minInnerHeight = 0 + var expandWidthCount = 0 + var expandHeightCount = 0 + + # Calculate minimum needed size: + for control in container.fChildControls: + if not control.visible: + continue + + if control.widthMode == WidthMode_Expand: + if container.layout == Layout_Horizontal: + minInnerWidth.inc(control.minWidth) + expandWidthCount.inc + + elif control.widthMode in {WidthMode_Auto , WidthMode_Static}: + if container.layout == Layout_Horizontal: + if control.wantedWidth == -1: + minInnerWidth.inc(control.minWidth) + expandWidthCount.inc + else: + minInnerWidth.inc(control.wantedWidth) + else: + if control.wantedWidth == -1: + expandWidthCount.inc + else: + minInnerWidth = max(minInnerWidth, control.wantedWidth) + + if control.heightMode == HeightMode_Expand: + if container.layout == Layout_Vertical: + minInnerHeight.inc(control.minHeight) + expandHeightCount.inc + + elif control.heightMode in {HeightMode_Auto, HeightMode_Static}: + if container.layout == Layout_Vertical: + if control.wantedHeight == -1: + minInnerHeight.inc(control.minHeight) + expandHeightCount.inc + else: + minInnerHeight.inc(control.wantedHeight) + else: + if control.wantedHeight == -1: + expandHeightCount.inc + else: + minInnerHeight = max(minInnerHeight, control.wantedHeight) + + # Add padding: + minInnerWidth.inc(container.padding * 2) + minInnerHeight.inc(container.padding * 2) + + # Add spacing: + if container.childControls.len > 1: + if container.layout == Layout_Horizontal: + minInnerWidth.inc(container.spacing * (container.childControls.len - 1)) + if container.layout == Layout_Vertical: + minInnerHeight.inc(container.spacing * (container.childControls.len - 1)) + + container.updateInnerSize(minInnerWidth, minInnerHeight) + + let innerWidth = max(minInnerWidth, clientWidth) + let innerHeight = max(minInnerHeight, clientHeight) + + # Calculate dynamic size: + var dynamicWidth = clientWidth - minInnerWidth + var dynamicHeight = clientHeight - minInnerHeight + dynamicWidth = max(dynamicWidth, 0) + dynamicHeight = max(dynamicHeight, 0) + + # Move and resize controls: + var x = container.padding + var y = container.padding + + if (container.xAlign == XAlign_Center or (container.xAlign == XAlign_Spread and container.childControls.len == 1)) and (container.layout == Layout_Vertical or expandWidthCount == 0): + x.inc(dynamicWidth div 2) + if (container.yAlign == YAlign_Center or (container.yAlign == YAlign_Spread and container.childControls.len == 1)) and (container.layout == Layout_Horizontal or expandHeightCount == 0): + y.inc(dynamicHeight div 2) + + for control in container.fChildControls: + if not control.visible: + continue + # echo " child: " & control.tag + + # Size: + var width = control.width + var height = control.height + # echo "size old: " & $width & ", " $height + if control.widthMode == WidthMode_Expand or control.wantedWidth == -1: + if container.layout == Layout_Horizontal: + if expandWidthCount > 0: + width = control.minWidth + dynamicWidth div expandWidthCount + else: + width = control.minWidth + dynamicWidth + else: + width = clientWidth - container.padding * 2 + elif control.widthMode == WidthMode_Auto: + width = control.wantedWidth + elif control.widthMode == WidthMode_Fill: + width = clientWidth - container.padding * 2 + + if control.minWidth > width: + width = control.minWidth + if control.maxWidth > 0 and control.maxWidth < width: + width = control.maxWidth + + if control.heightMode == HeightMode_Expand or control.wantedHeight == -1: + if container.layout == Layout_Vertical: + if expandHeightCount > 0: + height = control.minHeight + dynamicHeight div expandHeightCount + else: + height = control.minHeight + dynamicHeight + else: + height = clientHeight - container.padding * 2 + elif control.heightMode == HeightMode_Auto: + height = control.wantedHeight + elif control.heightMode == HeightMode_Fill: + height = clientHeight - container.padding * 2 + + if control.minHeight > height: + height = control.minHeight + if control.maxHeight > 0 and control.maxHeight < height: + height = control.maxHeight + + if control.width != width or control.height != height: + # echo " child: " & control.tag + # echo " new size: " & $width & ", " & $height + control.setSize(width, height) + + # Position: + if container.layout == Layout_Vertical or container.childControls.len == 1: + if container.xAlign == XAlign_Center: + x = (innerWidth - width) div 2 + elif container.xAlign == XAlign_Right: + x = innerWidth - width - container.padding + if container.layout == Layout_Horizontal or container.childControls.len == 1: + if container.yAlign == YAlign_Center: + y = (innerHeight - height) div 2 + elif container.yAlign == YAlign_Bottom: + y = innerHeight - height - container.padding + + if control.x != x or control.y != y: + # echo " new pos: " & $x & ", " $y + control.setPosition(x, y) + + # Calculate next position: + case container.layout + of Layout_Horizontal: + x.inc(width) + x.inc(container.spacing) + if container.xAlign == XAlign_Spread and expandWidthCount == 0 and container.childControls.len > 1: + x.inc(dynamicWidth div (container.childControls.len - 1)) + of Layout_Vertical: + y.inc(height) + y.inc(container.spacing) + if container.yAlign == YAlign_Spread and expandHeightCount == 0 and container.childControls.len > 1: + y.inc(dynamicHeight div (container.childControls.len - 1)) + + +# ---------------------------------------------------------------------------------------- +# Frame +# ---------------------------------------------------------------------------------------- + +proc newFrame(text = ""): Frame = + result = new NativeFrame + result.NativeFrame.init() + result.text = text + +proc init(frame: Frame) = + frame.ControlImpl.init() + frame.fText = "" + +method text(frame: Frame): string = frame.fText + +method `text=`(frame: Frame, text: string) = + frame.fText = text + frame.tag = text + # should be extended by NativeFrame + +method getPadding(frame: Frame): Spacing = + result.left = 4 + result.right = 4 + result.top = 4 + result.bottom = 4 + # should be extended by NativeFrame + +method `onDraw=`(container: NativeFrame, callback: DrawProc) = raiseError("NativeFrame does not allow onDraw.") + + +# ---------------------------------------------------------------------------------------- +# Button +# ---------------------------------------------------------------------------------------- + +proc newButton(text = ""): Button = + result = new NativeButton + result.NativeButton.init() + result.text = text + +proc init(button: Button) = + button.ControlImpl.init() + button.fText = "" + button.fOnClick = nil + button.fWidthMode = WidthMode_Auto + button.fHeightMode = HeightMode_Auto + button.minWidth = 15 + button.minHeight = 15 + +method text(button: Button): string = button.fText + +method `text=`(button: Button, text: string) = + button.fText = text + button.tag = text + button.triggerRelayoutIfModeIsAuto() + # should be extended by NativeButton + +method naturalWidth(button: Button): int = button.getTextWidth(button.text) + 20 + +method naturalHeight(button: Button): int = button.getTextLineHeight() * button.text.countLines + 12 + +method `onDraw=`(container: NativeButton, callback: DrawProc) = raiseError("NativeButton does not allow onDraw.") + + +# ---------------------------------------------------------------------------------------- +# Label +# ---------------------------------------------------------------------------------------- + +proc newLabel(text = ""): Label = + result = new NativeLabel + result.NativeLabel.init() + result.text = text + +proc init(label: Label) = + label.ControlImpl.init() + label.fText = "" + label.fWidthMode = WidthMode_Auto + label.fHeightMode = HeightMode_Auto + label.minWidth = 10 + label.minHeight = 10 + +method text(label: Label): string = label.fText + +method `text=`(label: Label, text: string) = + label.fText = text + label.tag = text + label.triggerRelayoutIfModeIsAuto() + +method naturalWidth(label: Label): int = label.getTextWidth(label.text) + +method naturalHeight(label: Label): int = label.getTextLineHeight() * label.text.countLines + +method `onDraw=`(container: NativeLabel, callback: DrawProc) = raiseError("NativeLabel does not allow onDraw.") + + +# ---------------------------------------------------------------------------------------- +# TextBox +# ---------------------------------------------------------------------------------------- + +proc newTextBox(text = ""): TextBox = + result = new NativeTextBox + result.NativeTextBox.init() + result.text = text + +proc init(textBox: TextBox) = + textBox.ControlImpl.init() + textBox.fWidthMode = WidthMode_Expand + textBox.fHeightMode = HeightMode_Auto + textBox.minWidth = 20 + textBox.minHeight = 20 + +method naturalHeight(textBox: TextBox): int = textBox.getTextLineHeight() + +method text(textBox: TextBox): string = discard + # has to be implemented by NativeTextBox + +method `text=`(textBox: TextBox, text: string) = discard + # has to be implemented by NativeTextBox + +method `onDraw=`(container: NativeTextBox, callback: DrawProc) = raiseError("NativeTextBox does not allow onDraw.") + + +# ---------------------------------------------------------------------------------------- +# TextArea +# ---------------------------------------------------------------------------------------- + +proc newTextArea(text = ""): TextArea = + result = new NativeTextArea + result.NativeTextArea.init() + result.text = text + +proc init(textArea: TextArea) = + textArea.ControlImpl.init() + textArea.fWidthMode = WidthMode_Expand + textArea.fHeightMode = HeightMode_Expand + textArea.minWidth = 20 + textArea.minHeight = 20 + textArea.wrap = true + +method text(textArea: TextArea): string = discard + # has to be implemented by NativeTextBox + +method `text=`(textArea: TextArea, text: string) = discard + # has to be implemented by NativeTextBox + +method addText(textArea: TextArea, text: string) = textArea.text = textArea.text & text + +method addLine(textArea: TextArea, text = "") = textArea.addtext(text & "\n") + +method scrollToBottom(textArea: TextArea) = discard + # has to be implemented by NativeTextBox + +method `onDraw=`(container: NativeTextArea, callback: DrawProc) = raiseError("NativeTextArea does not allow onDraw.") + +method wrap(textArea: TextArea): bool = textArea.fWrap + +method `wrap=`(textArea: TextArea, wrap: bool) = + textArea.fWrap = wrap + # should be extended by NativeTextArea + + +# ---------------------------------------------------------------------------------------- +# Platform-specific implementation +# ---------------------------------------------------------------------------------------- + +when useWindows(): include "nigui/private/windows/platform_impl" +when useGtk(): include "nigui/private/gtk3/platform_impl" diff --git a/src/nigui/private/gtk3/gtk3.nim b/src/nigui/private/gtk3/gtk3.nim new file mode 100755 index 0000000..f2c7eba --- /dev/null +++ b/src/nigui/private/gtk3/gtk3.nim @@ -0,0 +1,430 @@ +# NiGui - minimal GTK+ 3 binding + +{.deadCodeElim: on.} + +when defined(windows): + const libgtk3Path* = "libgtk-3-0.dll" +elif defined(gtk_quartz): + const libgtk3Path* = "libgtk-3.0.dylib" +elif defined(macosx): + const libgtk3Path* = "libgtk-x11-3.0.dylib" +else: + const libgtk3Path* = "libgtk-3.so(|.0)" + +{.pragma: libgtk3, cdecl, dynlib: libgtk3Path.} + +# ---------------------------------------------------------------------------------------- +# Types +# ---------------------------------------------------------------------------------------- + +type + GError* {.byCopy.} = object + domain*: int32 + code*: cint + message*: cstring + + GdkRectangle* {.byCopy.} = object + x*, y*: cint + width*, height*: cint + + GtkBorder* {.byCopy.} = object + left*: int16 + right*: int16 + top*: int16 + bottom*: int16 + + GdkRGBA* {.byCopy.} = object + red*: cdouble + green*: cdouble + blue*: cdouble + alpha*: cdouble + + GList* = ptr object + data*: pointer + next*: GList + prev*: GList + + GtkTargetEntry* {.byCopy.} = object + target*: cstring + flags*: cint + info*: cint + + GdkEventButton* {.byCopy.} = object + event_type*: cint + window*: pointer + send_event*: int8 + time*: cint + x*, y*: cdouble + axes*: ptr cdouble + state*: cint + button*: cint + device*: pointer + x_root*, y_root*: cdouble + + GdkEventKey* {.byCopy.} = object + event_type*: cint + window*: pointer + send_event*: int8 + time*: cint + state*: cint + keyval*: cint + length*: cint + `string`*: cstring + hardware_keycode*: int16 + group*: int8 + is_modifier*: int8 + + GtkTextIter* {.byCopy.} = object + dummy1: pointer + dummy2: pointer + dummy3: cint + dummy4: cint + dummy5: cint + dummy6: cint + dummy7: cint + dummy8: cint + dummy9: pointer + dummy10: pointer + dummy11: cint + dummy12: cint + dummy13: cint + dummy14: pointer + +# ---------------------------------------------------------------------------------------- +# Constants +# ---------------------------------------------------------------------------------------- + +const + # GtkWindowType: + GTK_WINDOW_TOPLEVEL* = 0 + GTK_WINDOW_POPUP* = 1 + + # GtkDestDefaults: + # [..] + GTK_DEST_DEFAULT_ALL* = 7 + + # GdkDragAction: + GDK_ACTION_DEFAULT* = 1 + GDK_ACTION_COPY* = 2 + GDK_ACTION_MOVE* = 4 + GDK_ACTION_LINK* = 8 + GDK_ACTION_PRIVATE* = 16 + GDK_ACTION_ASK* = 32 + + # GtkOrientation: + GTK_ORIENTATION_HORIZONTAL* = 0 + GTK_ORIENTATION_VERTICAL* = 1 + + # GtkWrapMode: + GTK_WRAP_NONE* = 0 + GTK_WRAP_CHAR* = 1 + GTK_WRAP_WORD* = 2 + GTK_WRAP_WORD_CHAR* = 3 + + # GtkPolicyType: + GTK_POLICY_ALWAYS* = 0 + GTK_POLICY_AUTOMATIC* = 1 + GTK_POLICY_NEVER* = 2 + GTK_POLICY_EXTERNAL* = 3 + + # PangoEllipsizeMode: + PANGO_ELLIPSIZE_NONE* = 0 + PANGO_ELLIPSIZE_START* = 1 + PANGO_ELLIPSIZE_MIDDLE* = 2 + PANGO_ELLIPSIZE_END* = 3 + + # GtkButtonBoxStyle: + GTK_BUTTONBOX_SPREAD* = 0 + GTK_BUTTONBOX_EDGE* = 1 + GTK_BUTTONBOX_START* = 2 + GTK_BUTTONBOX_END* = 3 + GTK_BUTTONBOX_CENTER* = 4 + GTK_BUTTONBOX_EXPAND* = 5 + + # GtkJustification: + GTK_JUSTIFY_LEFT* = 0 + GTK_JUSTIFY_RIGHT* = 1 + GTK_JUSTIFY_CENTER* = 2 + GTK_JUSTIFY_FILL* = 3 + + # GtkStateFlags: + GTK_STATE_FLAG_NORMAL* = 0 + # [..] + + # GdkEventMask: + GDK_BUTTON_PRESS_MASK* = 256 + GDK_BUTTON_RELEASE_MASK* = 512 + GDK_KEY_PRESS_MASK* = 1024 + # [..] + + # cairo_format_t: + CAIRO_FORMAT_ARGB32* = 0 + # [..] + + # GtkFileChooserAction: + GTK_FILE_CHOOSER_ACTION_OPEN* = 0 + GTK_FILE_CHOOSER_ACTION_SAVE* = 1 + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER* = 2 + GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER* = 3 + + # GtkResponseType: + GTK_RESPONSE_NONE* = -1 + GTK_RESPONSE_REJECT* = -2 + GTK_RESPONSE_ACCEPT* = -3 + GTK_RESPONSE_DELETE_EVENT* = -4 + GTK_RESPONSE_OK* = -5 + GTK_RESPONSE_CANCEL* = -6 + GTK_RESPONSE_CLOSE* = -7 + GTK_RESPONSE_YES* = -8 + GTK_RESPONSE_NO* = -9 + GTK_RESPONSE_APPLY* = -10 + GTK_RESPONSE_HELP* = -11 + +# ---------------------------------------------------------------------------------------- +# General Gtk Procs +# ---------------------------------------------------------------------------------------- + +proc g_slist_length*(list: pointer): int {.importc: "g_slist_length", libgtk3.} +proc g_slist_nth_data_string*(list: pointer, n: int): cstring {.importc: "g_slist_nth_data", libgtk3.} + +proc gtk_init*(argc, argv: pointer) {.importc: "gtk_init", libgtk3.} + +proc gtk_main*() {.importc: "gtk_main", libgtk3.} +proc gtk_main_quit*() {.importc: "gtk_main_quit", libgtk3.} +proc gtk_events_pending*(): cint {.importc: "gtk_events_pending", libgtk3.} +proc gtk_main_iteration*(): cint {.importc: "gtk_main_iteration", libgtk3.} +proc g_timeout_add*(interval: cint, function, data: pointer): cint {.importc: "g_timeout_add", libgtk3.} +proc g_source_remove*(tag: cint): bool {.importc: "g_source_remove", libgtk3.} +proc g_signal_connect_data*(instance: pointer, detailed_signal: cstring, c_handler: pointer, data, destroy_data, connect_flags: pointer = nil): pointer {.importc: "g_signal_connect_data", libgtk3.} + +proc gtk_window_new*(`type`: cint): pointer {.importc: "gtk_window_new", libgtk3.} +proc gtk_window_set_title*(window: pointer, title: cstring) {.importc: "gtk_window_set_title", libgtk3.} +# proc gtk_window_get_title*(window: pointer): cstring {.importc: "gtk_window_get_title", libgtk3.} +proc gtk_window_set_transient_for*(window, parent: pointer) {.importc: "gtk_window_set_transient_for", libgtk3.} +proc gtk_window_set_modal*(window: pointer, modal: cint) {.importc: "gtk_window_set_modal", libgtk3.} +# proc gtk_window_set_default_size*(window: pointer, width, height: cint) {.importc: "gtk_window_set_default_size", libgtk3.} +proc gtk_window_resize*(window: pointer, width, height: cint) {.importc: "gtk_window_resize", libgtk3.} +proc gtk_window_resize_to_geometry*(window: pointer, width, height: cint) {.importc: "gtk_window_resize_to_geometry", libgtk3.} +proc gtk_window_get_size*(window: pointer, width, height: var cint) {.importc: "gtk_window_get_size", libgtk3.} +proc gtk_window_get_position*(window: pointer, x, y: var cint) {.importc: "gtk_window_get_position", libgtk3.} +proc gtk_window_move*(window: pointer, x, y: cint) {.importc: "gtk_window_move", libgtk3.} +proc gtk_window_set_icon_from_file*(window: pointer, filename: cstring, err: pointer) {.importc: "gtk_window_set_icon_from_file", libgtk3.} + +proc gdk_window_begin_paint_rect*(window: pointer, rectangle: var GdkRectangle) {.importc: "gdk_window_begin_paint_rect", libgtk3.} +proc gdk_window_begin_paint_region*(window: pointer, region: pointer) {.importc: "gdk_window_begin_paint_region", libgtk3.} +proc gdk_window_end_paint*(window: pointer) {.importc: "gdk_window_end_paint", libgtk3.} +proc gdk_window_get_clip_region*(window: pointer): pointer {.importc: "gdk_window_get_clip_region", libgtk3.} + +proc gtk_widget_destroy*(widget: pointer) {.importc: "gtk_widget_destroy", libgtk3.} +proc gtk_widget_show*(widget: pointer) {.importc: "gtk_widget_show", libgtk3.} +proc gtk_widget_hide*(widget: pointer) {.importc: "gtk_widget_hide", libgtk3.} +proc gtk_widget_set_size_request*(widget: pointer, width, height: cint) {.importc: "gtk_widget_set_size_request", libgtk3.} +proc gtk_widget_size_allocate*(widget: pointer, allocation: var GdkRectangle) {.importc: "gtk_widget_size_allocate", libgtk3.} +proc gtk_widget_get_size_request*(widget: pointer, width, height: var cint) {.importc: "gtk_widget_get_size_request", libgtk3.} +proc gtk_widget_get_allocation*(widget: pointer, allocation: var GdkRectangle) {.importc: "gtk_widget_get_allocation", libgtk3.} +# proc gtk_widget_set_allocation*(widget: pointer, allocation: var GdkRectangle) {.importc: "gtk_widget_set_allocation", libgtk3.} +# proc gtk_widget_set_hexpand*(widget: pointer, expand: cint) {.importc: "gtk_widget_set_hexpand", libgtk3.} +proc gtk_widget_queue_draw*(widget: pointer) {.importc: "gtk_widget_queue_draw", libgtk3.} +proc gtk_widget_set_margin_top*(widget: pointer, margin: cint) {.importc: "gtk_widget_set_margin_top", libgtk3.} +proc gtk_widget_add_events*(widget: pointer, events: cint) {.importc: "gtk_widget_add_events", libgtk3.} +proc gtk_widget_set_can_focus*(widget: pointer, can_focus: cint) {.importc: "gtk_widget_set_can_focus", libgtk3.} +proc gtk_widget_modify_font*(widget: pointer, font_desc: pointer) {.importc: "gtk_widget_modify_font", libgtk3.} +proc gtk_widget_override_color*(widget: pointer, state: cint, color: var GdkRGBA) {.importc: "gtk_widget_override_color", libgtk3.} +proc gtk_widget_override_background_color*(widget: pointer, state: cint, color: var GdkRGBA) {.importc: "gtk_widget_override_background_color", libgtk3.} +proc gtk_widget_get_path*(widget: pointer): pointer {.importc: "gtk_widget_get_path", libgtk3.} +proc gtk_widget_style_get*(widget: pointer, first_property_name: cstring, value: pointer, passNil: pointer) {.importc: "gtk_widget_style_get", libgtk3.} +proc gtk_widget_get_style_context*(widget: pointer): pointer {.importc: "gtk_widget_get_style_context", libgtk3.} +proc gtk_widget_grab_focus*(widget: pointer) {.importc: "gtk_widget_grab_focus", libgtk3.} +proc gtk_widget_is_focus*(widget: pointer): bool {.importc: "gtk_widget_is_focus", libgtk3.} +proc gtk_widget_realize*(widget: pointer) {.importc: "gtk_widget_realize", libgtk3.} +proc gtk_widget_draw*(widget, cr: pointer) {.importc: "gtk_widget_draw", libgtk3.} + +proc gtk_container_add*(container, widget: pointer) {.importc: "gtk_container_add", libgtk3.} +proc gtk_container_remove*(container, widget: pointer) {.importc: "gtk_container_remove", libgtk3.} +# proc gtk_container_foreach*(container, callback, callback_data: pointer) {.importc: "gtk_container_foreach", libgtk3.} +proc gtk_container_get_children*(container: pointer): GList {.importc: "gtk_container_get_children", libgtk3.} +proc gtk_container_set_border_width*(container: pointer, width: cint) {.importc: "gtk_container_set_border_width", libgtk3.} + +proc gtk_fixed_new*(): pointer {.importc: "gtk_fixed_new", libgtk3.} +proc gtk_fixed_move*(fixed, widget: pointer, x, y: cint) {.importc: "gtk_fixed_move", libgtk3.} + +proc gtk_layout_new*(hadjustment, vadjustment: pointer): pointer {.importc: "gtk_layout_new", libgtk3.} +# proc gtk_layout_put*(layout, child_widget: pointer, x, y: cint) {.importc: "gtk_layout_put", libgtk3.} +proc gtk_layout_move*(layout, child_widget: pointer, x, y: cint) {.importc: "gtk_layout_move", libgtk3.} +# proc gtk_layout_set_size*(layout: pointer, width, height: cint) {.importc: "gtk_layout_set_size", libgtk3.} +# proc gtk_layout_get_hadjustment*(layout: pointer): pointer {.importc: "gtk_layout_get_hadjustment", libgtk3.} +# proc gtk_layout_get_vadjustment*(layout: pointer): pointer {.importc: "gtk_layout_get_vadjustment", libgtk3.} + +# proc gtk_scrollbar_new*(orientation: GtkOrientation, adjustment: pointer): pointer {.importc: "gtk_scrollbar_new", libgtk3.} + +proc gtk_dialog_new*(): pointer {.importc: "gtk_dialog_new", libgtk3.} +proc gtk_dialog_run*(dialog: pointer): cint {.importc: "gtk_dialog_run", libgtk3.} +proc gtk_dialog_add_button*(dialog: pointer, button_text: cstring, response_id: cint): pointer {.importc: "gtk_dialog_add_button", libgtk3.} +proc gtk_dialog_get_content_area*(dialog: pointer): pointer {.importc: "gtk_dialog_get_content_area", libgtk3.} +proc gtk_dialog_get_action_area*(dialog: pointer): pointer {.importc: "gtk_dialog_get_action_area", libgtk3.} + +proc gtk_file_chooser_dialog_new*(title: string, parent: pointer, action: int, text1: cstring, response1: int, text2: cstring, response2: int, ending: pointer): pointer {.importc: "gtk_file_chooser_dialog_new", libgtk3.} +proc gtk_file_chooser_set_current_name*(chooser: pointer, name: cstring): bool {.importc: "gtk_file_chooser_set_current_name", libgtk3.} +proc gtk_file_chooser_get_filename*(chooser: pointer): cstring {.importc: "gtk_file_chooser_get_filename", libgtk3.} +proc gtk_file_chooser_get_filenames*(chooser: pointer): pointer {.importc: "gtk_file_chooser_get_filenames", libgtk3.} +proc gtk_file_chooser_set_select_multiple*(chooser: pointer, select_multiple: bool) {.importc: "gtk_file_chooser_set_select_multiple", libgtk3.} +proc gtk_file_chooser_set_current_folder*(chooser: pointer, filename: cstring): bool {.importc: "gtk_file_chooser_set_current_folder", libgtk3.} + +proc gtk_button_box_set_layout*(widget: pointer, layout_style: cint) {.importc: "gtk_button_box_set_layout", libgtk3.} + +# proc gtk_message_dialog_new*(parent: pointer, flags: GtkDialogFlags, `type`: GtkMessageType, buttons: GtkButtonsType, message_format: cstring): pointer {.importc: "gtk_message_dialog_new", libgtk3.} + +proc gtk_label_new*(str: cstring): pointer {.importc: "gtk_label_new", libgtk3.} +proc gtk_label_set_text*(label: pointer, str: cstring) {.importc: "gtk_label_set_text", libgtk3.} +# proc gtk_label_get_text*(label: pointer): cstring {.importc: "gtk_label_get_text", libgtk3.} +proc gtk_label_set_ellipsize*(label: pointer, mode: cint) {.importc: "gtk_label_set_ellipsize", libgtk3.} +# proc gtk_label_set_justify*(label: pointer, jtype: cint) {.importc: "gtk_label_set_justify", libgtk3.} +proc gtk_label_set_xalign*(label: pointer, xalign: cfloat) {.importc: "gtk_label_set_xalign", libgtk3.} +proc gtk_label_set_yalign*(label: pointer, yalign: cfloat) {.importc: "gtk_label_set_yalign", libgtk3.} + +# proc gtk_box_new*(orientation: GtkOrientation, spacing: cint): pointer {.importc: "gtk_box_new", libgtk3.} +proc gtk_box_pack_start*(box, child: pointer, expand, fill: cint, padding: cint) {.importc: "gtk_box_pack_start", libgtk3.} + +proc gtk_button_new*(): pointer {.importc: "gtk_button_new", libgtk3.} +# proc gtk_button_new_with_label*(label: cstring): pointer {.importc: "gtk_button_new_with_label", libgtk3.} +# proc gtk_button_get_label*(button: pointer): cstring {.importc: "gtk_button_get_label", libgtk3.} +proc gtk_button_set_label*(button: pointer, label: cstring) {.importc: "gtk_button_set_label", libgtk3.} + +proc gtk_entry_new*(): pointer {.importc: "gtk_entry_new", libgtk3.} +proc gtk_entry_set_text*(entry: pointer, text: cstring) {.importc: "gtk_entry_set_text", libgtk3.} +proc gtk_entry_get_text*(entry: pointer): cstring {.importc: "gtk_entry_get_text", libgtk3.} +proc gtk_entry_set_width_chars*(entry: pointer, n_chars: cint) {.importc: "gtk_entry_set_width_chars", libgtk3.} + +proc gtk_text_view_new*(): pointer {.importc: "gtk_text_view_new", libgtk3.} +proc gtk_text_view_set_buffer*(text_view, buffer: pointer) {.importc: "gtk_text_view_set_buffer", libgtk3.} +proc gtk_text_view_get_buffer*(text_view: pointer): pointer {.importc: "gtk_text_view_get_buffer", libgtk3.} +proc gtk_text_view_set_wrap_mode*(text_view: pointer, wrap_mode: cint) {.importc: "gtk_text_view_set_wrap_mode", libgtk3.} +proc gtk_text_view_set_left_margin*(text_view: pointer, margin: cint) {.importc: "gtk_text_view_set_left_margin", libgtk3.} +proc gtk_text_view_set_right_margin*(text_view: pointer, margin: cint) {.importc: "gtk_text_view_set_right_margin", libgtk3.} +proc gtk_text_view_set_top_margin*(text_view: pointer, margin: cint) {.importc: "gtk_text_view_set_top_margin", libgtk3.} +proc gtk_text_view_set_bottom_margin*(text_view: pointer, margin: cint) {.importc: "gtk_text_view_set_bottom_margin", libgtk3.} +proc gtk_text_view_scroll_to_iter*(text_view: pointer, iter: var GtkTextIter, within_margin: cfloat, use_align: bool, xalign, yalign: cfloat) {.importc: "gtk_text_view_scroll_to_iter", libgtk3.} +# proc gtk_text_view_scroll_to_mark*(text_view, mark: pointer, within_margin: cfloat, use_align: bool, xalign, yalign: cfloat) {.importc: "gtk_text_view_scroll_to_mark", libgtk3.} +# proc gtk_text_view_place_cursor_onscreen*(text_view: pointer): bool {.importc: "gtk_text_view_place_cursor_onscreen", libgtk3.} + +# proc gtk_text_mark_new*(name: cstring, left_gravity: bool): pointer {.importc: "gtk_text_mark_new", libgtk3.} + +# proc gtk_text_buffer_new*(table: pointer): pointer {.importc: "gtk_text_buffer_new", libgtk3.} +proc gtk_text_buffer_set_text*(text_buffer: pointer, text: cstring, len: cint) {.importc: "gtk_text_buffer_set_text", libgtk3.} +proc gtk_text_buffer_get_text*(text_buffer: pointer, start, `end`: var GtkTextIter, include_hidden_chars: cint): cstring {.importc: "gtk_text_buffer_get_text", libgtk3.} +proc gtk_text_buffer_get_start_iter*(text_buffer: pointer, iter: var GtkTextIter) {.importc: "gtk_text_buffer_get_start_iter", libgtk3.} +proc gtk_text_buffer_get_end_iter*(text_buffer: pointer, iter: var GtkTextIter) {.importc: "gtk_text_buffer_get_end_iter", libgtk3.} +# proc gtk_text_buffer_add_mark*(buffer, mark: pointer, where: var GtkTextIter) {.importc: "gtk_text_buffer_add_mark", libgtk3.} +# proc gtk_text_buffer_get_insert*(buffer: pointer): pointer {.importc: "gtk_text_buffer_get_insert", libgtk3.} +# proc gtk_text_buffer_get_iter_at_line*(buffer: pointer, iter: var GtkTextIter, line_number: cint) {.importc: "gtk_text_buffer_get_iter_at_line", libgtk3.} +proc gtk_text_buffer_insert*(buffer: pointer, iter: var GtkTextIter, text: cstring, len: cint) {.importc: "gtk_text_buffer_insert", libgtk3.} + +proc gtk_scrolled_window_new*(hadjustment, vadjustment: pointer): pointer {.importc: "gtk_scrolled_window_new", libgtk3.} +proc gtk_scrolled_window_set_policy*(scrolled_window: pointer, hscrollbar_policy, vscrollbar_policy: cint) {.importc: "gtk_scrolled_window_set_policy", libgtk3.} +proc gtk_scrolled_window_get_hscrollbar*(scrolled_window: pointer): pointer {.importc: "gtk_scrolled_window_get_hscrollbar", libgtk3.} +proc gtk_scrolled_window_get_vscrollbar*(scrolled_window: pointer): pointer {.importc: "gtk_scrolled_window_get_vscrollbar", libgtk3.} +proc gtk_scrolled_window_get_hadjustment*(scrolled_window: pointer): pointer {.importc: "gtk_scrolled_window_get_hadjustment", libgtk3.} +proc gtk_scrolled_window_get_vadjustment*(scrolled_window: pointer): pointer {.importc: "gtk_scrolled_window_get_vadjustment", libgtk3.} +# proc gtk_scrolled_window_get_max_content_width*(scrolled_window: pointer): cint {.importc: "gtk_scrolled_window_get_max_content_width", libgtk3.} +# proc gtk_scrolled_window_get_min_content_width*(scrolled_window: pointer): cint {.importc: "gtk_scrolled_window_get_min_content_width", libgtk3.} +# proc gtk_scrolled_window_set_overlay_scrolling*(scrolled_window: pointer, overlay_scrolling: bool) {.importc: "gtk_scrolled_window_set_overlay_scrolling", libgtk3.} + +proc gtk_frame_new*(label: cstring): pointer {.importc: "gtk_frame_new", libgtk3.} +proc gtk_frame_set_label*(frame: pointer, label: cstring) {.importc: "gtk_frame_set_label", libgtk3.} +proc gtk_frame_get_label_widget*(frame: pointer): pointer {.importc: "gtk_frame_get_label_widget", libgtk3.} + +proc gtk_drawing_area_new*(): pointer {.importc: "gtk_drawing_area_new", libgtk3.} + +proc gtk_adjustment_get_value*(adjustment: pointer): cdouble {.importc: "gtk_adjustment_get_value", libgtk3.} +proc gtk_adjustment_set_value*(adjustment: pointer, value: cdouble) {.importc: "gtk_adjustment_set_value", libgtk3.} +proc gtk_adjustment_set_upper*(adjustment: pointer, upper: cdouble) {.importc: "gtk_adjustment_set_upper", libgtk3.} +proc gtk_adjustment_get_upper*(adjustment: pointer): cdouble {.importc: "gtk_adjustment_get_upper", libgtk3.} +proc gtk_adjustment_set_page_size*(adjustment: pointer, page_size: cdouble) {.importc: "gtk_adjustment_set_page_size", libgtk3.} +proc gtk_adjustment_get_page_size*(adjustment: pointer): cdouble {.importc: "gtk_adjustment_get_page_size", libgtk3.} + +proc gtk_drag_dest_set*(widget: pointer, flags: cint, targets: pointer, n_targets: cint, actions: cint) {.importc: "gtk_drag_dest_set", libgtk3.} + +proc gdk_keyval_to_unicode*(keyval: cint): cint {.importc: "gdk_keyval_to_unicode", libgtk3.} + +proc gdk_screen_get_default*(): pointer {.importc: "gdk_screen_get_default", libgtk3.} +proc gdk_screen_get_primary_monitor*(screen: pointer): cint {.importc: "gdk_screen_get_primary_monitor", libgtk3.} +# proc gdk_screen_get_width*(screen: pointer): cint {.importc: "gdk_screen_get_width", libgtk3.} +# proc gdk_screen_get_height*(screen: pointer): cint {.importc: "gdk_screen_get_height", libgtk3.} +proc gdk_screen_get_monitor_workarea*(screen: pointer, monitor_num: cint, dest: var GdkRectangle) {.importc: "gdk_screen_get_monitor_workarea", libgtk3.} + +proc gtk_style_context_get_padding*(context: pointer, state: cint, padding: var GtkBorder) {.importc: "gtk_style_context_get_padding", libgtk3.} +proc gtk_style_context_get_background_color*(context: pointer, state: cint, color: var GdkRGBA) {.importc: "gtk_style_context_get_background_color", libgtk3.} +proc gtk_style_context_get_color*(context: pointer, state: cint, color: var GdkRGBA) {.importc: "gtk_style_context_get_color", libgtk3.} +# proc gtk_style_context_get_font*(context: pointer, state: cint): pointer {.importc: "gtk_style_context_get_font", libgtk3.} + +proc gtk_border_new*(): pointer {.importc: "gtk_border_new", libgtk3.} + +# proc gdk_threads_init*() {.importc: "gdk_threads_init", libgtk3.} +# proc gdk_threads_add_idle*(function, data: pointer): cint {.importc: "gdk_threads_add_idle", libgtk3.} + +proc gtk_scrollbar_new*(orientation: cint, adjustment: pointer): pointer {.importc: "gtk_scrollbar_new", libgtk3.} + +proc gtk_adjustment_new*(value, lower, upper, step_increment, page_increment, page_size: cdouble): pointer {.importc: "gtk_adjustment_new", libgtk3.} + +# proc gtk_selection_data_get_length*(selection_data: pointer): cint {.importc: "gtk_selection_data_get_length", libgtk3.} +# proc gtk_selection_data_get_text*(selection_data: pointer): cstring {.importc: "gtk_selection_data_get_text", libgtk3.} + +proc gtk_selection_data_get_uris*(selection_data: pointer): ptr cstring {.importc: "gtk_selection_data_get_uris", libgtk3.} +proc g_filename_from_uri*(uri: pointer): cstring {.importc: "g_filename_from_uri", libgtk3.} + + +# ---------------------------------------------------------------------------------------- +# Drawing Related Procs +# ---------------------------------------------------------------------------------------- + +proc gtk_widget_create_pango_layout*(widget: pointer, text: cstring): pointer {.importc: "gtk_widget_create_pango_layout", libgtk3.} +proc gdk_cairo_set_source_rgba*(cr: pointer, rgba: var GdkRGBA) {.importc: "gdk_cairo_set_source_rgba", libgtk3.} +proc gdk_cairo_surface_create_from_pixbuf*(pixbuf: pointer, scale: cint, for_window: pointer): pointer {.importc: "gdk_cairo_surface_create_from_pixbuf", libgtk3.} +proc gdk_pixbuf_new_from_file*(filename: cstring, error: pointer): pointer {.importc: "gdk_pixbuf_new_from_file", libgtk3.} +proc gdk_pixbuf_save*(pixbuf: pointer, filename, `type`: cstring, error: pointer, param5, param6, param7: cstring): bool {.importc: "gdk_pixbuf_save", libgtk3.} +proc gdk_pixbuf_get_from_surface*(surface: pointer, src_x, src_y, width, height: cint): pointer {.importc: "gdk_pixbuf_get_from_surface", libgtk3.} +# proc gdk_pixmap_create_from_data*(drawable, data: pointer, width, height, depth: cint, fg, bg: var GdkRGBA): pointer {.importc: "gdk_pixmap_create_from_data", libgtk3.} + +proc cairo_image_surface_create*(format: cint, width, height: cint): pointer {.importc: "cairo_image_surface_create", libgtk3.} +# proc cairo_image_surface_create_for_data*(data: pointer, format: cairo_format_t, width, height, stride: cint): pointer {.importc: "cairo_image_surface_create_for_data", libgtk3.} +proc cairo_image_surface_get_width*(surface: pointer): cint {.importc: "cairo_image_surface_get_width", libgtk3.} +proc cairo_image_surface_get_height*(surface: pointer): cint {.importc: "cairo_image_surface_get_height", libgtk3.} +proc cairo_image_surface_get_stride*(surface: pointer): cint {.importc: "cairo_image_surface_get_stride", libgtk3.} +proc cairo_image_surface_get_data*(surface: pointer): cstring {.importc: "cairo_image_surface_get_data", libgtk3.} +proc cairo_surface_flush*(surface: pointer) {.importc: "cairo_surface_flush", libgtk3.} +proc cairo_surface_mark_dirty*(surface: pointer) {.importc: "cairo_surface_mark_dirty", libgtk3.} +proc cairo_surface_destroy*(surface: pointer) {.importc: "cairo_surface_destroy", libgtk3.} + +# proc cairo_format_stride_for_width*(format: cairo_format_t, width: cint): cint {.importc: "cairo_format_stride_for_width", libgtk3.} + +proc cairo_create*(target: pointer): pointer {.importc: "cairo_create", libgtk3.} +proc cairo_get_target*(cr: pointer): pointer {.importc: "cairo_get_target", libgtk3.} +proc cairo_set_source_rgb*(cr: pointer, red, green, blue: cdouble) {.importc: "cairo_set_source_rgb", libgtk3.} +proc cairo_set_source_surface*(cr, surface: pointer, x, y: cdouble) {.importc: "cairo_set_source_surface", libgtk3.} +proc cairo_fill*(cr: pointer) {.importc: "cairo_fill", libgtk3.} +proc cairo_stroke*(cr: pointer) {.importc: "cairo_stroke", libgtk3.} +proc cairo_rectangle*(cr: pointer, x, y, width, height: cdouble) {.importc: "cairo_rectangle", libgtk3.} +proc cairo_line_to*(cr: pointer, x, y: cdouble) {.importc: "cairo_line_to", libgtk3.} +proc cairo_move_to*(cr: pointer, x, y: cdouble) {.importc: "cairo_move_to", libgtk3.} +proc cairo_set_line_width*(cr: pointer, width: cdouble) {.importc: "cairo_set_line_width", libgtk3.} +# proc cairo_image_surface_create_from_png*(filename: cstring): pointer {.importc: "cairo_image_surface_create_from_png", libgtk3.} +proc cairo_paint*(cr: pointer) {.importc: "cairo_paint", libgtk3.} +proc cairo_scale*(cr: pointer, x, y: cdouble) {.importc: "cairo_scale", libgtk3.} +proc cairo_translate*(cr: pointer, tx, ty: cdouble) {.importc: "cairo_translate", libgtk3.} +# proc cairo_set_antialias*(cr: pointer, antialias: cint) {.importc: "cairo_set_antialias", libgtk3.} +proc cairo_save*(cr: pointer) {.importc: "cairo_save", libgtk3.} +proc cairo_restore*(cr: pointer) {.importc: "cairo_restore", libgtk3.} + +proc pango_cairo_show_layout*(cr, layout: pointer) {.importc: "pango_cairo_show_layout", libgtk3.} +proc pango_cairo_create_layout*(cr: pointer): pointer {.importc: "pango_cairo_create_layout", libgtk3.} +proc pango_layout_set_text*(layout: pointer, text: cstring, length: cint) {.importc: "pango_layout_set_text", libgtk3.} +proc pango_layout_get_pixel_size*(layout: pointer, width, height: var cint) {.importc: "pango_layout_get_pixel_size", libgtk3.} +proc pango_layout_set_font_description*(layout, desc: pointer) {.importc: "pango_layout_set_font_description", libgtk3.} +proc pango_font_description_new*(): pointer {.importc: "pango_font_description_new", libgtk3.} +proc pango_font_description_set_family*(desc: pointer, family: cstring) {.importc: "pango_font_description_set_family", libgtk3.} +proc pango_font_description_set_size*(desc: pointer, size: cint) {.importc: "pango_font_description_set_size", libgtk3.} +# proc pango_font_description_get_size*(desc: pointer): cint {.importc: "pango_font_description_get_size", libgtk3.} +# proc pango_layout_set_markup*(layout: pointer, markup: cstring, length: cint) {.importc: "pango_layout_set_markup", libgtk3.} +# proc pango_layout_new*(context: pointer): pointer {.importc: "pango_layout_new", libgtk3.} + + diff --git a/src/nigui/private/gtk3/platform_impl.nim b/src/nigui/private/gtk3/platform_impl.nim new file mode 100755 index 0000000..fbb7b45 --- /dev/null +++ b/src/nigui/private/gtk3/platform_impl.nim @@ -0,0 +1,1176 @@ +# NiGui - GTK+ 3 platform-specific code - part 3 + +# This file will be included in "nigui.nim". + +# Imports: +# math, os, strutils, times are imported by nigui.nim +import gtk3 +import tables +import hashes + + +# ---------------------------------------------------------------------------------------- +# Internal Things +# ---------------------------------------------------------------------------------------- + +const pFontSizeFactor = 700 + +# needed to calculate clicks: +var pLastMouseButtonDownControl: ControlImpl +var pLastMouseButtonDownControlX: int +var pLastMouseButtonDownControlY: int + +proc pRaiseGError(error: ptr GError) = + if error == nil: + raiseError("Unkown error") + raiseError($error.message, false) + +proc pColorToGdkRGBA(color: Color, rgba: var GdkRGBA) = + rgba.red = color.red.float / 255 + rgba.green = color.green.float / 255 + rgba.blue = color.blue.float / 255 + rgba.alpha = color.alpha.float / 255 + +proc pGdkRGBAToColor(rgba: var GdkRGBA): Color = + result.red = uint8(rgba.red.float * 255) + result.green = uint8(rgba.green.float * 255) + result.blue = uint8(rgba.blue.float * 255) + result.alpha = uint8(rgba.alpha.float * 255) + +proc pWindowDeleteSignal(widgetHandle, event, data: pointer): bool {.cdecl.} = + let window = cast[WindowImpl](data) + window.dispose() + return true + +proc pWindowConfigureSignal(windowHandle, event, data: pointer): bool {.cdecl.} = + # called on resize and move + let window = cast[WindowImpl](data) + var x, y: cint + gtk_window_get_position(window.fHandle, x, y) + window.fX = x + window.fY = y + var width, height: cint + gtk_window_get_size(window.fHandle, width, height) + window.fWidth = width + window.fHeight = height + window.fClientWidth = width + window.fClientHeight = height + window.triggerRelayout() + +proc pKeyvalToKey(keyval: cint): 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 97: Key_A + of 98: Key_B + of 99: Key_C + of 100: Key_D + of 101: Key_E + of 102: Key_F + of 103: Key_G + of 104: Key_H + of 105: Key_I + of 106: Key_J + of 107: Key_K + of 108: Key_L + of 109: Key_M + of 110: Key_N + of 111: Key_O + of 112: Key_P + of 113: Key_Q + of 114: Key_R + of 115: Key_S + of 116: Key_T + of 117: Key_U + of 118: Key_V + of 119: Key_W + of 120: Key_X + of 121: Key_Y + of 122: Key_Z + of 32: Key_Space + of 65289: Key_Tab + of 65293: Key_Return + of 65307: Key_Escape + of 65379: Key_Insert + of 65535: Key_Delete + of 65288: Key_Backspace + of 65361: Key_Left + of 65362: Key_Up + of 65363: Key_Right + of 65364: Key_Down + of 65360: Key_Home + of 65367: Key_End + of 65365: Key_PageUp + of 65366: Key_PageDown + else: Key_None + +proc pWindowKeyPressSignal(widget: pointer, event: var GdkEventKey, data: pointer): bool {.cdecl.} = + # echo "window keyPressCallback" + + # echo event.keyval + # echo event.hardware_keycode + + # echo $gdk_keyval_to_unicode(event.keyval) + var unicode = gdk_keyval_to_unicode(event.keyval) + # if unicode == 0: + # unicode = event.keyval + + let window = cast[WindowImpl](data) + var evt = new WindowKeyEvent + evt.window = window + evt.key = pKeyvalToKey(event.keyval) + if evt.key == Key_None: + echo "Unkown key value: ", event.keyval + return + + evt.character = $event.`string` + evt.unicode = unicode + + try: + window.handleKeyDownEvent(evt) + except: + handleException() + +proc pControlKeyPressSignal(widget: pointer, event: var GdkEventKey, data: pointer): bool {.cdecl.} = + + # echo "control keyPressCallback" + + # echo $gdk_keyval_to_unicode(event.keyval) + var unicode = gdk_keyval_to_unicode(event.keyval) + # if unicode == 0: + # unicode = event.keyval + + let control = cast[ControlImpl](data) + var evt = new ControlKeyEvent + evt.control = control + evt.key = pKeyvalToKey(event.keyval) + if evt.key == Key_None: + echo "Unkown key value: ", event.keyval + return + evt.character = $event.`string` + evt.unicode = unicode + + try: + control.handleKeyDownEvent(evt) + except: + handleException() + + return evt.cancel + +method focus(control: ControlImpl) = + gtk_widget_grab_focus(control.fHandle) + +method focus(control: NativeTextArea) = + gtk_widget_grab_focus(control.fTextViewHandle) + +proc pDefaultControlButtonPressSignal(widget: pointer, event: var GdkEventButton, data: pointer): bool {.cdecl.} = + let control = cast[ControlImpl](data) + let x = event.x.int + let y = event.y.int + var evt = new MouseButtonEvent + evt.control = control + case event.button + of 1: evt.button = MouseButton_Left + of 2: evt.button = MouseButton_Middle + of 3: evt.button = MouseButton_Right + else: return # TODO + evt.x = x + evt.y = y + + try: + control.handleMouseButtonDownEvent(evt) + except: + handleException() + + pLastMouseButtonDownControl = control + pLastMouseButtonDownControlX = x + pLastMouseButtonDownControlY = y + +proc pCustomControlButtonPressSignal(widget: pointer, event: var GdkEventButton, data: pointer): bool {.cdecl.} = + discard pDefaultControlButtonPressSignal(widget, event, data) + let control = cast[ControlImpl](data) + control.focus() + result = true # Stop propagation, required to detect clicks + +proc pControlButtonReleaseSignal(widget: pointer, event: var GdkEventButton, data: pointer): bool {.cdecl.} = + let control = cast[ControlImpl](data) + let x = event.x.int + let y = event.y.int + if not (x >= 0 and x < control.width and y >= 0 and y < control.height): + return + var evt = new MouseButtonEvent + evt.control = control + case event.button + of 1: evt.button = MouseButton_Left + of 2: evt.button = MouseButton_Middle + of 3: evt.button = MouseButton_Right + else: return # TODO + evt.x = x + evt.y = y + control.handleMouseButtonUpEvent(evt) + if event.button == 1 and control == pLastMouseButtonDownControl and abs(x - pLastMouseButtonDownControlX) <= clickMaxXYMove and abs(y - pLastMouseButtonDownControlY) <= clickMaxXYMove: + var clickEvent = new ClickEvent + clickEvent.control = control + try: + control.handleClickEvent(clickEvent) + except: + handleException() + # result = true # stop propagation + +proc pControlChangedSignal(widget: pointer, data: pointer): bool {.cdecl.} = + let control = cast[ControlImpl](data) + var evt = new TextChangeEvent + try: + control.handleTextChangeEvent(evt) + except: + handleException() + +# proc pTextAreaEndUserActionSignal(widget: pointer, data: pointer): bool {.cdecl.} = + # let control = cast[ControlImpl](data) + # discard + +proc pSetDragDest(widget: pointer) = + var target: GtkTargetEntry + target.target = "text/uri-list" + target.flags = 0 + target.info = 0 + gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_ALL, target.addr, 1, GDK_ACTION_COPY) + + +# ---------------------------------------------------------------------------------------- +# App Procedures +# ---------------------------------------------------------------------------------------- + +proc init(app: App) = + gtk_init(nil, nil) + + # Determine default styles: + var window = gtk_window_new(GTK_WINDOW_TOPLEVEL) + var context = gtk_widget_get_style_context(window) + var rgba: GdkRGBA + gtk_style_context_get_background_color(context, GTK_STATE_FLAG_NORMAL, rgba) + app.defaultBackgroundColor = rgba.pGdkRGBAToColor() + gtk_style_context_get_color(context, GTK_STATE_FLAG_NORMAL, rgba) + app.defaultTextColor = rgba.pGdkRGBAToColor() + gtk_widget_destroy(window) + +proc runMainLoop() = gtk_main() + +proc processEvents(app: App) = + while gtk_events_pending() == 1: + discard gtk_main_iteration() + + +# ---------------------------------------------------------------------------------------- +# Dialogs +# ---------------------------------------------------------------------------------------- + +proc alert(window: Window, message: string, title = "Message") = + var dialog = gtk_dialog_new() + gtk_window_set_title(dialog, title) + gtk_window_resize(dialog, 200, 70) + let contentArea = gtk_dialog_get_content_area(dialog) + gtk_container_set_border_width(contentArea, 15) + var label = gtk_label_new(message) + gtk_widget_show(label) + gtk_box_pack_start(contentArea, label, 0, 0, 0) + let actionArea = gtk_dialog_get_action_area(dialog) + gtk_button_box_set_layout(actionArea, GTK_BUTTONBOX_EXPAND) + gtk_widget_set_margin_top(actionArea, 15) + discard gtk_dialog_add_button(dialog, "OK", 1) + if window != nil: + gtk_window_set_transient_for(dialog, cast[WindowImpl](window).fHandle) + discard gtk_dialog_run(dialog) + gtk_widget_hide(dialog) + +method run*(dialog: OpenFileDialog) = + dialog.files = @[] + var chooser = gtk_file_chooser_dialog_new(nil, nil, GTK_FILE_CHOOSER_ACTION_OPEN, "Cancel", GTK_RESPONSE_CANCEL, "Open", GTK_RESPONSE_ACCEPT, nil) + # Issue: When a title is passed, the dialog is shown without a title + discard gtk_file_chooser_set_current_folder(chooser, dialog.directory) + gtk_file_chooser_set_select_multiple(chooser, dialog.multiple) + let res = gtk_dialog_run(chooser) + if res == GTK_RESPONSE_ACCEPT: + let list = gtk_file_chooser_get_filenames(chooser) + let count = g_slist_length(list) + for i in 0..count - 1: + dialog.files.add($g_slist_nth_data_string(list, i)) + gtk_widget_destroy(chooser) + +method run(dialog: SaveFileDialog) = + var chooser = gtk_file_chooser_dialog_new(nil, nil, GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", GTK_RESPONSE_CANCEL, "Save", GTK_RESPONSE_ACCEPT, nil) + # Issue: When a title is passed, the dialog is shown without a title + let res = gtk_dialog_run(chooser) + discard gtk_file_chooser_set_current_folder(chooser, dialog.directory) + if dialog.defaultName.len > 0: + discard gtk_file_chooser_set_current_name(chooser, dialog.defaultName) # Issue: does not work + if res == GTK_RESPONSE_ACCEPT: + dialog.file = $gtk_file_chooser_get_filename(chooser) + else: + dialog.file = "" + gtk_widget_destroy(chooser) + + +# ---------------------------------------------------------------------------------------- +# Timers +# ---------------------------------------------------------------------------------------- + +type TimerEntry = object + timerInternalId: cint + timerProc: TimerProc + data: pointer + +var + pTimers = initTable[int64, TimerEntry]() + pNextTimerId: int = 1 + +proc pTimerFunction(timer: Timer): bool {.cdecl.} = + let timerEntry = pTimers.getOrDefault(cast[int](timer)) + var event = new TimerEvent + event.timer = timer + event.data = timerEntry.data + timerEntry.timerProc(event) + pTimers.del(cast[int](timer)) + # result is false to stop timer + +proc pRepeatingTimerFunction(timer: Timer): bool {.cdecl.} = + let timerEntry = pTimers.getOrDefault(cast[int](timer)) + var event = new TimerEvent + event.timer = timer + event.data = timerEntry.data + timerEntry.timerProc(event) + result = true # repeat timer + +proc startTimer(milliSeconds: int, timerProc: TimerProc, data: pointer = nil): Timer = + var timerEntry: TimerEntry + timerEntry.timerInternalId = g_timeout_add(milliSeconds.cint, pTimerFunction, cast[pointer](pNextTimerId)) + timerEntry.timerProc = timerProc + timerEntry.data = data + pTimers[pNextTimerId] = timerEntry + result = cast[Timer](pNextTimerId) + pNextTimerId.inc() + if pNextTimerId == inactiveTimer: + pNextTimerId.inc() + +proc startRepeatingTimer(milliSeconds: int, timerProc: TimerProc, data: pointer = nil): Timer = + var timerEntry: TimerEntry + timerEntry.timerInternalId = g_timeout_add(milliSeconds.cint, pRepeatingTimerFunction, cast[pointer](pNextTimerId)) + timerEntry.timerProc = timerProc + timerEntry.data = data + pTimers[pNextTimerId] = timerEntry + result = cast[Timer](pNextTimerId) + pNextTimerId.inc() + if pNextTimerId == inactiveTimer: + pNextTimerId.inc() + +proc stop(timer: var Timer) = + if cast[int](timer) != inactiveTimer: + let timerEntry = pTimers.getOrDefault(cast[int](timer)) + pTimers.del(cast[int](timer)) + discard g_source_remove(timerEntry.timerInternalId) + timer = cast[Timer](inactiveTimer) + +# ---------------------------------------------------------------------------------------- +# Canvas +# ---------------------------------------------------------------------------------------- + +proc pUpdateFont(canvas: Canvas) = + let canvasImpl = cast[CanvasImpl](canvas) + canvasImpl.fFont = pango_font_description_new() + pango_font_description_set_family(canvasImpl.fFont, canvas.fontFamily) + pango_font_description_set_size(canvasImpl.fFont, cint(canvas.fontSize * pFontSizeFactor)) + +method drawText(canvas: Canvas, text: string, x, y = 0) = + let canvasImpl = cast[CanvasImpl](canvas) + let cr = canvasImpl.fCairoContext + if cr == nil: + raiseError("Canvas is not in drawing state.") + var rgba: GdkRGBA + canvas.textColor.pColorToGdkRGBA(rgba) + gdk_cairo_set_source_rgba(cr, rgba) + + var layout = pango_cairo_create_layout(cr) + pango_layout_set_text(layout, text, text.len.cint) + + if canvasImpl.fFont == nil: + canvas.pUpdateFont() + pango_layout_set_font_description(layout, canvasImpl.fFont) + + cairo_move_to(cr, x.float, y.float) + pango_cairo_show_layout(cr, layout) + +method drawLine(canvas: Canvas, x1, y1, x2, y2: int) = + let cr = cast[CanvasImpl](canvas).fCairoContext + if cr == nil: + raiseError("Canvas is not in drawing state.") + var rgba: GdkRGBA + canvas.lineColor.pColorToGdkRGBA(rgba) + gdk_cairo_set_source_rgba(cr, rgba) + cairo_move_to(cr, x1.float, y1.float) + cairo_line_to(cr, x2.float, y2.float) + cairo_stroke(cr) + +method drawRectArea(canvas: Canvas, x, y, width, height: int) = + let cr = cast[CanvasImpl](canvas).fCairoContext + if cr == nil: + raiseError("Canvas is not in drawing state.") + var rgba: GdkRGBA + canvas.areaColor.pColorToGdkRGBA(rgba) + gdk_cairo_set_source_rgba(cr, rgba) + cairo_rectangle(cr, x.float, y.float, width.float, height.float) + cairo_fill(cr) + +method drawRectOutline(canvas: Canvas, x, y, width, height: int) = + let cr = cast[CanvasImpl](canvas).fCairoContext + if cr == nil: + raiseError("Canvas is not in drawing state.") + var rgba: GdkRGBA + canvas.lineColor.pColorToGdkRGBA(rgba) + gdk_cairo_set_source_rgba(cr, rgba) + cairo_rectangle(cr, x.float, y.float, width.float, height.float) + cairo_set_line_width(cr, 1) + cairo_stroke(cr) + +method drawImage(canvas: Canvas, image: Image, x, y = 0, width, height = -1) = + let cr = cast[CanvasImpl](canvas).fCairoContext + if cr == nil: + raiseError("Canvas is not in drawing state.") + let imageCanvas = cast[CanvasImpl](image.canvas) + if imageCanvas.fSurface == nil: + raiseError("Image is not initialized.") + var drawWith = image.width + var drawHeight = image.height + if width != -1: + drawWith = width + drawHeight = int(drawHeight * drawWith / image.width) + if height != -1: + drawHeight = height + if drawWith == image.width and drawHeight == image.height: + cairo_set_source_surface(cr, imageCanvas.fSurface, x.cdouble, y.cdouble) + cairo_paint(cr) + else: + cairo_save(cr) + cairo_translate(cr, x.cdouble, y.cdouble) + cairo_scale(cr, drawWith / image.width, drawHeight / image.height) + cairo_set_source_surface(cr, imageCanvas.fSurface, 0, 0) + cairo_paint(cr) + cairo_restore(cr) + +method setPixel(canvas: Canvas, x, y: int, color: Color) = + let canvasImpl = cast[CanvasImpl](canvas) + let cr = canvasImpl.fCairoContext + if cr == nil: + raiseError("Canvas is not in drawing state.") + if canvasImpl.fData == nil: + # For a Canvas of a Control we can't access the memory directly, so draw a rectangle (slower) + var rgba: GdkRGBA + color.pColorToGdkRGBA(rgba) + gdk_cairo_set_source_rgba(cr, rgba) + cairo_rectangle(cr, x.float, y.float, 1, 1) + cairo_fill(cr) + else: + # For a Canvas of an Image we can write in the memory directly (faster) + if x < 0 or y < 0 or x >= canvas.width or y >= canvas.height: + raiseError("Pixel is out of range.") + cairo_surface_flush(canvasImpl.fSurface) + let i = y * canvasImpl.fStride + x * 4 + canvasImpl.fData[i + 0] = color.blue.chr + canvasImpl.fData[i + 1] = color.green.chr + canvasImpl.fData[i + 2] = color.red.chr + canvasImpl.fData[i + 3] = 255.chr + cairo_surface_mark_dirty(canvasImpl.fSurface) + +method `fontFamily=`(canvas: CanvasImpl, fontFamily: string) = + procCall canvas.Canvas.`fontFamily=`(fontFamily) + canvas.fFont = nil + +method `fontSize=`(canvas: CanvasImpl, fontSize: int) = + procCall canvas.Canvas.`fontSize=`(fontSize) + canvas.fFont = nil + +method getTextLineWidth(canvas: CanvasImpl, text: string): int = + if canvas.fCairoContext == nil: + raiseError("Canvas is not in drawing state.") + var layout = pango_cairo_create_layout(canvas.fCairoContext) + pango_layout_set_text(layout, text, text.len.cint) + if canvas.fFont == nil: + canvas.pUpdateFont() + pango_layout_set_font_description(layout, canvas.fFont) + var width: cint = 0 + var height: cint = 0 + pango_layout_get_pixel_size(layout, width, height) + result = width + +method getTextLineHeight(canvas: CanvasImpl): int = + if canvas.fCairoContext == nil: + raiseError("Canvas is not in drawing state.") + var layout = pango_cairo_create_layout(canvas.fCairoContext) + pango_layout_set_text(layout, "a", 1) + if canvas.fFont == nil: + canvas.pUpdateFont() + pango_layout_set_font_description(layout, canvas.fFont) + var width: cint = 0 + var height: cint = 0 + pango_layout_get_pixel_size(layout, width, height) + result = height + + +# ---------------------------------------------------------------------------------------- +# Image +# ---------------------------------------------------------------------------------------- + +method resize(image: Image, width, height: int) = + let canvas = cast[CanvasImpl](image.fCanvas) + if canvas.fSurface != nil: + cairo_surface_destroy(canvas.fSurface) + canvas.fSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width.cint, height.cint) + canvas.fCairoContext = cairo_create(canvas.fSurface) + canvas.fData = cairo_image_surface_get_data(canvas.fSurface) + canvas.fStride = cairo_image_surface_get_stride(canvas.fSurface) + image.canvas.fWidth = width + image.canvas.fHeight = height + +method loadFromFile(image: Image, filePath: string) = + let canvas = cast[CanvasImpl](image.fCanvas) + if canvas.fSurface != nil: + cairo_surface_destroy(canvas.fSurface) + var error: ptr GError + var pixbuf = gdk_pixbuf_new_from_file(filePath, error.addr) + if pixbuf == nil: + pRaiseGError(error) + canvas.fSurface = gdk_cairo_surface_create_from_pixbuf(pixbuf, 1, nil) + canvas.fCairoContext = cairo_create(canvas.fSurface) + canvas.fData = cairo_image_surface_get_data(canvas.fSurface) + canvas.fStride = cairo_image_surface_get_stride(canvas.fSurface) + image.canvas.fWidth = cairo_image_surface_get_width(canvas.fSurface) + image.canvas.fHeight = cairo_image_surface_get_height(canvas.fSurface) + +method saveToPngFile(image: Image, filePath: string) = + let canvas = cast[CanvasImpl](image.fCanvas) + var pixbuf = gdk_pixbuf_get_from_surface(canvas.fSurface, 0, 0, image.width.cint, image.height.cint) + var error: ptr GError + if not gdk_pixbuf_save(pixbuf, filePath, "png", error.addr, nil, nil, nil): + pRaiseGError(error) + +method saveToJpegFile(image: Image, filePath: string, quality = 80) = + let canvas = cast[CanvasImpl](image.fCanvas) + var pixbuf = gdk_pixbuf_get_from_surface(canvas.fSurface, 0, 0, image.width.cint, image.height.cint) + var error: ptr GError + if not gdk_pixbuf_save(pixbuf, filePath, "jpeg", error.addr, "quality", $quality, nil): + pRaiseGError(error) + + +# ---------------------------------------------------------------------------------------- +# Window +# ---------------------------------------------------------------------------------------- + +proc pWindowDragDataReceivedSignal(widget, context: pointer, x, y: cint, data: pointer, info, time: cint, user_data: pointer) {.cdecl.} = + let window = cast[WindowImpl](user_data) + var files: seq[string] = @[] + var p = gtk_selection_data_get_uris(data) + while p[] != nil: + files.add($g_filename_from_uri(p[])) + p = cast[ptr cstring](cast[int](p) + 8) + var event = new DropFilesEvent + event.window = window + event.files = files + window.handleDropFilesEvent(event) + +proc pMainScrollbarDraw(widget: pointer, cr: pointer, data: pointer): bool {.cdecl.} = + # This proc is there to get the scrollbar size + if fScrollbarSize == -1: + var scrollbar = gtk_scrolled_window_get_hscrollbar(widget) + var allocation: GdkRectangle + gtk_widget_get_allocation(scrollbar, allocation) + gtk_scrolled_window_set_policy(widget, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC) + fScrollbarSize = allocation.height + +proc init(window: WindowImpl) = + window.fHandle = gtk_window_new(GTK_WINDOW_TOPLEVEL) + window.fInnerHandle = gtk_scrolled_window_new(nil, nil) + gtk_widget_show(window.fInnerHandle) + gtk_container_add(window.fHandle, window.fInnerHandle) + window.Window.init() + discard g_signal_connect_data(window.fHandle, "delete-event", pWindowDeleteSignal, cast[pointer](window)) + discard g_signal_connect_data(window.fHandle, "configure-event", pWindowConfigureSignal, cast[pointer](window)) + discard g_signal_connect_data(window.fHandle, "key-press-event", pWindowKeyPressSignal, cast[pointer](window)) + + # Enable drag and drop of files: + pSetDragDest(window.fHandle) + discard g_signal_connect_data(window.fHandle, "drag-data-received", pWindowDragDataReceivedSignal, cast[pointer](window)) + + if fScrollbarSize == -1: + gtk_scrolled_window_set_policy(window.fInnerHandle, GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS) + discard g_signal_connect_data(window.fInnerHandle, "draw", pMainScrollbarDraw, nil) + +method destroy(window: WindowImpl) = + procCall window.Window.destroy() + gtk_widget_destroy(window.fHandle) + # this destroys also child widgets + window.fHandle = nil + +method `visible=`(window: WindowImpl, visible: bool) = + procCall window.Window.`visible=`(visible) + if visible: + gtk_widget_show(window.fHandle) + while fScrollbarSize == -1: + discard gtk_main_iteration() + else: + gtk_widget_hide(window.fHandle) + app.processEvents() + +method showModal(window, parent: WindowImpl) = + gtk_window_set_modal(window.fHandle, 1) + gtk_window_set_transient_for(window.fHandle, parent.fHandle) + window.visible = true + +method `width=`*(window: WindowImpl, width: int) = + procCall window.Window.`width=`(width) + gtk_window_resize(window.fHandle, window.width.cint, window.height.cint) + window.fClientWidth = window.width + +method `height=`*(window: WindowImpl, height: int) = + procCall window.Window.`height=`(height) + gtk_window_resize(window.fHandle, window.width.cint, window.height.cint) + window.fClientHeight = window.height + +proc pUpdatePosition(window: WindowImpl) = gtk_window_move(window.fHandle, window.x.cint, window.y.cint) + +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 screen = gdk_screen_get_default() + let monitor = gdk_screen_get_primary_monitor(screen) + var rect: GdkRectangle + gdk_screen_get_monitor_workarea(screen, monitor, rect) + window.fX = rect.x + (rect.width - window.width) div 2 + window.fY = rect.y + (rect.height - window.height) div 2 + window.pUpdatePosition() + +method `title=`(window: WindowImpl, title: string) = + procCall window.Window.`title=`(title) + gtk_window_set_title(window.fHandle, window.title.cstring) + +method `control=`(window: WindowImpl, control: ControlImpl) = + procCall window.Window.`control=`(control) + gtk_container_add(window.fInnerHandle, control.fHandle) + +method `iconPath=`(window: WindowImpl, iconPath: string) = + procCall window.Window.`iconPath=`(iconPath) + gtk_window_set_icon_from_file(window.fHandle, iconPath, nil) + + +# ---------------------------------------------------------------------------------------- +# Control +# ---------------------------------------------------------------------------------------- + +method pUpdateScrollBar(control: ControlImpl) + +proc pControlDrawSignal(widget: pointer, cr: pointer, data: pointer): bool {.cdecl.} = + let control = cast[ControlImpl](data) + + # Trigger pUpdateScrollBar() in case it's not initialized, which could be because of missing fScrollbarSize + if control.fHScrollbar == nil: + control.pUpdateScrollBar() + + var event = new DrawEvent + event.control = control + var canvas = cast[CanvasImpl](control.canvas) + if canvas == nil: + canvas = newCanvas(control) + canvas.fCairoContext = cr + # canvas.fSurface = cairo_get_target(cr) # no need to set this + # canvas.fData = cairo_image_surface_get_data(canvas.fSurface) # does not work + try: + # var region = gdk_window_get_clip_region(control.fHandle) + # gdk_window_begin_paint_region(control.fHandle, region) + # no effect + + control.handleDrawEvent(event) + + # gdk_window_end_paint(control.fHandle) + except: + handleException() + + canvas.fCairoContext = nil + +proc pControlScollXSignal(adjustment: pointer, data: pointer) {.cdecl.} = + let control = cast[ControlImpl](data) + control.fXScrollPos = gtk_adjustment_get_value(adjustment).int + control.forceRedraw() + +proc pControlScollYSignal(adjustment: pointer, data: pointer) {.cdecl.} = + let control = cast[ControlImpl](data) + control.fYScrollPos = gtk_adjustment_get_value(adjustment).int + control.forceRedraw() + +proc pUpdateFont(control: ControlImpl) = + var font = pango_font_description_new() + pango_font_description_set_family(font, control.fontFamily) + pango_font_description_set_size(font, cint(control.fontSize * pFontSizeFactor)) + gtk_widget_modify_font(control.fHandle, font) + var rgba: GdkRGBA + control.textColor.pColorToGdkRGBA(rgba) + gtk_widget_override_color(control.fHandle, GTK_STATE_FLAG_NORMAL, rgba) + +method pAddButtonPressEvent(control: ControlImpl) = + gtk_widget_add_events(control.fHandle, GDK_BUTTON_PRESS_MASK) + discard g_signal_connect_data(control.fHandle, "button-press-event", pCustomControlButtonPressSignal, cast[pointer](control)) + +proc init(control: ControlImpl) = + + if control.fHandle == nil: + # Direct instance of ControlImpl: + control.fHandle = gtk_layout_new(nil, nil) + discard g_signal_connect_data(control.fHandle, "draw", pControlDrawSignal, cast[pointer](control)) + + gtk_widget_add_events(control.fHandle, GDK_KEY_PRESS_MASK) + discard g_signal_connect_data(control.fHandle, "key-press-event", pControlKeyPressSignal, cast[pointer](control)) + + control.pAddButtonPressEvent() + + gtk_widget_add_events(control.fHandle, GDK_BUTTON_RELEASE_MASK) + discard g_signal_connect_data(control.fHandle, "button-release-event", pControlButtonReleaseSignal, cast[pointer](control)) + + procCall control.Control.init() + +method destroy(control: ControlImpl) = + procCall control.Control.destroy() + gtk_widget_destroy(control.fHandle) + # this destroys also child widgets + +method `visible=`(control: ControlImpl, visible: bool) = + procCall control.Control.`visible=`(visible) + if visible: + gtk_widget_show(control.fHandle) + else: + gtk_widget_hide(control.fHandle) + +proc dummy(widget: pointer, event: var GdkEventButton, data: pointer): bool {.cdecl.} = + echo "dummy" + result = true # Stop propagation + +method pUpdateScrollBar(control: ControlImpl) = + if control.fScrollableWidth == -1 and control.fScrollableHeight == -1: + return + echo "control.pUpdateScrollBar" + if control.fHScrollbar == nil: + if fScrollbarSize == -1: + return + # Init scrolling: + # echo "fScrollbarSize ", fScrollbarSize + control.fHAdjust = gtk_adjustment_new(0, 0, 0, 10, 10, 0) + control.fVAdjust = gtk_adjustment_new(0, 0, 0, 10, 10, 0) + control.fHScrollbar = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, control.fHAdjust) + control.fVScrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, control.fVAdjust) + gtk_container_add(control.fHandle, control.fHScrollbar) + gtk_container_add(control.fHandle, control.fVScrollbar) + discard g_signal_connect_data(control.fHAdjust, "value-changed", pControlScollXSignal, cast[pointer](control)) + discard g_signal_connect_data(control.fVAdjust, "value-changed", pControlScollYSignal, cast[pointer](control)) + + # The dead corner is an area which just needs to be covered with a control without function and the default background color + control.fDeadCornerHandle = gtk_label_new("") + # control.fDeadCornerHandle = gtk_fixed_new() + gtk_container_add(control.fHandle, control.fDeadCornerHandle) + var rgba: GdkRGBA + pColorToGdkRGBA(app.defaultBackgroundColor, rgba) + gtk_widget_override_background_color(control.fDeadCornerHandle, GTK_STATE_FLAG_NORMAL, rgba) + gtk_widget_set_size_request(control.fDeadCornerHandle, fScrollbarSize.cint, fScrollbarSize.cint) + + # Prevent that a click on the dead corner triggers the onClick event of the Control: + # gtk_widget_add_events(control.fDeadCornerHandle, GDK_BUTTON_PRESS_MASK) + # discard g_signal_connect_data(control.fDeadCornerHandle, "button-press-event", dummy, nil) + # gtk_widget_add_events(control.fDeadCornerHandle, GDK_BUTTON_RELEASE_MASK) + # discard g_signal_connect_data(control.fDeadCornerHandle, "button-release-event", dummy, nil) + # TODO: does not work. try EventBox + + # 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 + + # Update horizontal scrollbar: + if control.fXScrollEnabled: + var upper = control.scrollableWidth + var size = control.width + if control.fYScrollEnabled: + upper.inc(fScrollbarSize) + size.dec(fScrollbarSize) + gtk_adjustment_set_upper(control.fHAdjust, upper.cdouble) + let value = gtk_adjustment_get_value(control.fHAdjust).int + let maxValue = upper - control.width + if value > maxValue: + gtk_adjustment_set_value(control.fHAdjust, maxValue.float) + gtk_adjustment_set_page_size(control.fHAdjust, control.width.float) + gtk_widget_set_size_request(control.fHScrollbar, size.cint, 0) + gtk_layout_move(control.fHandle, control.fHScrollbar, 0, (control.height - fScrollbarSize).cint) + gtk_widget_show(control.fHScrollbar) + # Ensure that scroll pos is within range: + control.fXScrollPos = max(min(control.fXScrollPos, maxValue), 0) + else: + gtk_widget_hide(control.fHScrollbar) + control.fXScrollPos = 0 + + # Update vertical scrollbar: + if control.fYScrollEnabled: + var upper = control.scrollableHeight + var size = control.height + if control.fXScrollEnabled: + upper.inc(fScrollbarSize) + size.dec(fScrollbarSize) + gtk_adjustment_set_upper(control.fVAdjust, upper.cdouble) + let value = gtk_adjustment_get_value(control.fVAdjust).int + let maxValue = upper - control.height + if value > maxValue: + gtk_adjustment_set_value(control.fVAdjust, maxValue.float) + gtk_adjustment_set_page_size(control.fVAdjust, control.height.float) + gtk_widget_set_size_request(control.fVScrollbar, 0, size.cint) + gtk_layout_move(control.fHandle, control.fVScrollbar, (control.width - fScrollbarSize).cint, 0) + gtk_widget_show(control.fVScrollbar) + # Ensure that scroll pos is within range: + control.fYScrollPos = max(min(control.fYScrollPos, maxValue), 0) + else: + gtk_widget_hide(control.fVScrollbar) + control.fYScrollPos = 0 + + # Update dead corner: + if control.fXScrollEnabled and control.fYScrollEnabled: + gtk_layout_move(control.fHandle, control.fDeadCornerHandle, (control.width - fScrollbarSize).cint, (control.height - fScrollbarSize).cint) + gtk_widget_show(control.fDeadCornerHandle) + else: + gtk_widget_hide(control.fDeadCornerHandle) + + +method setSize(control: ControlImpl, width, height: int) = + if width == control.fWidth and height == control.fHeight: + return + procCall control.Control.setSize(width, height) + gtk_widget_set_size_request(control.fHandle, width.cint, height.cint) + pUpdateScrollBar(control) + +method setPosition(control: ControlImpl, x, y: int) = + procCall control.Control.setPosition(x, y) + if control.fParentControl != nil: + gtk_fixed_move(cast[ContainerImpl](control.fParentControl).fInnerHandle, control.fHandle, x.cint, y.cint) + # gtk_layout_move(cast[ContainerImpl](control.fParentControl).fHandle, control.fHandle, x.cint, y.cint) + +method forceRedraw(control: ControlImpl) = gtk_widget_queue_draw(control.fHandle) + +# proc removeWidgetInternal(container: WidgetContainer, widget: Widget) = gtk_container_remove(container.innerHandle, widget.handle) + +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 setTextColor(control: ControlImpl, color: Color) = + procCall control.Control.setTextColor(color) + control.pUpdateFont() + +method `setBackgroundColor=`(control: ControlImpl, color: Color) = + procCall control.Control.setBackgroundColor(color) + var rgba: GdkRGBA + color.pColorToGdkRGBA(rgba) + gtk_widget_override_background_color(control.fHandle, GTK_STATE_FLAG_NORMAL, rgba) + # TODO: check why it has no effect + +method getTextLineWidth(control: ControlImpl, text: string): int = + var layout = gtk_widget_create_pango_layout(control.fHandle, text) + var width: cint = 0 + var height: cint = 0 + pango_layout_get_pixel_size(layout, width, height) + result = width + +method getTextLineHeight(control: ControlImpl): int = + var layout = gtk_widget_create_pango_layout(control.fHandle, "a") + var width: cint = 0 + var height: cint = 0 + pango_layout_get_pixel_size(layout, width, height) + result = height + +discard """ method `xScrollPos=`(control: ControlImpl, xScrollPos: int) = + procCall control.Control.`xScrollPos=`(xScrollPos) + control.pUpdateScrollBar() + +method `yScrollPos=`(control: ControlImpl, yScrollPos: int) = + procCall control.Control.`yScrollPos=`(yScrollPos) + control.pUpdateScrollBar() """ + +method `scrollableWidth=`(control: ControlImpl, scrollableWidth: int) = + if scrollableWidth == control.fScrollableWidth: + return + procCall control.Control.`scrollableWidth=`(scrollableWidth) + control.pUpdateScrollBar() + +method `scrollableHeight=`(control: ControlImpl, scrollableHeight: int) = + if scrollableHeight == control.fScrollableHeight: + return + procCall control.Control.`scrollableHeight=`(scrollableHeight) + control.pUpdateScrollBar() + + +# ---------------------------------------------------------------------------------------- +# Container +# ---------------------------------------------------------------------------------------- + +proc init(container: ContainerImpl) = + container.fHandle = gtk_fixed_new() + # ScrollWnd: + container.fScrollWndHandle = gtk_scrolled_window_new(nil, nil) + gtk_widget_show(container.fScrollWndHandle) + gtk_container_add(container.fHandle, container.fScrollWndHandle) + # Inner: + container.fInnerHandle = gtk_fixed_new() + gtk_widget_show(container.fInnerHandle) + gtk_container_add(container.fScrollWndHandle, container.fInnerHandle) + container.Container.init() + +method pUpdateScrollWnd(container: ContainerImpl) = + let padding = container.getPadding() + let width = container.width - padding.left - padding.right + let height = container.height - padding.top - padding.bottom + gtk_widget_set_size_request(container.fScrollWndHandle, width.cint, height.cint) + gtk_fixed_move(container.fHandle, container.fScrollWndHandle, padding.left.cint, padding.top.cint) + +method `frame=`(container: ContainerImpl, frame: Frame) = + procCall container.Container.`frame=`(frame) + if frame != nil: + gtk_container_add(container.fHandle, frame.fHandle) + container.pUpdateScrollWnd() + +method add(container: ContainerImpl, control: ControlImpl) = + gtk_container_add(container.fInnerHandle, control.fHandle) + procCall container.Container.add(control) + +method paddingLeft(container: ContainerImpl): int = 5 # TODO +method paddingRight(container: ContainerImpl): int = 5 # TODO +method paddingTop(container: ContainerImpl): int = 15 # TODO +method paddingBottom(container: ContainerImpl): int = 5 # TODO + +method setInnerSize(container: ContainerImpl, width, height: int) = + procCall container.Container.setInnerSize(width, height) + gtk_widget_set_size_request(container.fInnerHandle, width.cint, height.cint) + +method setSize(container: ContainerImpl, width, height: int) = + procCall container.Container.setSize(width, height) + container.pUpdateScrollWnd() + +method pUpdateScrollBar(container: ContainerImpl) = + # Overwrite base method + if container.fScrollableWidth == -1 and container.fScrollableHeight == -1: + return + # echo "container.pUpdateScrollBar" + + var xPolicy: cint = GTK_POLICY_NEVER + var yPolicy: cint = GTK_POLICY_NEVER + if container.fXScrollEnabled: + xPolicy = GTK_POLICY_AUTOMATIC + if container.fYScrollEnabled: + yPolicy = GTK_POLICY_AUTOMATIC + gtk_scrolled_window_set_policy(container.fScrollWndHandle, xPolicy, yPolicy) + + +# ---------------------------------------------------------------------------------------- +# Frame +# ---------------------------------------------------------------------------------------- + +proc init(frame: NativeFrame) = + frame.fHandle = gtk_frame_new("") + frame.Frame.init() + +method `text=`(frame: NativeFrame, text: string) = + procCall frame.Frame.`text=`(text) + gtk_frame_set_label(frame.fHandle, text) + +method getPadding(frame: NativeFrame): Spacing = + result = procCall frame.Frame.getPadding() + result.top = frame.getTextLineHeight() * frame.text.countLines + 2 + + +# ---------------------------------------------------------------------------------------- +# Button +# ---------------------------------------------------------------------------------------- + +proc init(button: NativeButton) = + button.fHandle = gtk_button_new() + button.Button.init() + # discard g_signal_connect_data(button.fHandle, "clicked", pWidgetClickSignal, cast[pointer](button)) + +method `text=`(button: NativeButton, text: string) = + procCall button.Button.`text=`(text) + gtk_button_set_label(button.fHandle, text) + # Don't let the button expand: + let list = gtk_container_get_children(button.fHandle) + if list != nil: + gtk_label_set_ellipsize(list.data, PANGO_ELLIPSIZE_END) + app.processEvents() + +method naturalWidth(button: NativeButton): int = + # Override parent method, to make it big enough for the text to fit in. + var context = gtk_widget_get_style_context(button.fHandle) + var padding: GtkBorder + gtk_style_context_get_padding(context, GTK_STATE_FLAG_NORMAL, padding) + result = button.getTextLineWidth(button.text) + padding.left + padding.right + 5 + +method pAddButtonPressEvent(control: NativeButton) = + gtk_widget_add_events(control.fHandle, GDK_BUTTON_PRESS_MASK) + discard g_signal_connect_data(control.fHandle, "button-press-event", pDefaultControlButtonPressSignal, cast[pointer](control)) + + +# ---------------------------------------------------------------------------------------- +# Label +# ---------------------------------------------------------------------------------------- + +proc init(label: NativeLabel) = + label.fHandle = gtk_label_new("") + gtk_label_set_xalign(label.fHandle, 0) + gtk_label_set_yalign(label.fHandle, 0.5) + gtk_label_set_ellipsize(label.fHandle, PANGO_ELLIPSIZE_END) + label.Label.init() + +method `text=`(label: NativeLabel, text: string) = + procCall label.Label.`text=`(text) + gtk_label_set_text(label.fHandle, text) + app.processEvents() + +method naturalWidth(label: NativeLabel): int = label.getTextLineWidth(label.text) + 10 +# Override parent method, to make it big enough for the text to fit in. + +method pAddButtonPressEvent(control: NativeLabel) = + gtk_widget_add_events(control.fHandle, GDK_BUTTON_PRESS_MASK) + discard g_signal_connect_data(control.fHandle, "button-press-event", pDefaultControlButtonPressSignal, cast[pointer](control)) + + +# ---------------------------------------------------------------------------------------- +# TextBox +# ---------------------------------------------------------------------------------------- + +proc init(textBox: NativeTextBox) = + textBox.fHandle = gtk_entry_new() + discard g_signal_connect_data(textBox.fHandle, "changed", pControlChangedSignal, cast[pointer](textBox)) + textBox.TextBox.init() + +method text(textBox: NativeTextBox): string = $gtk_entry_get_text(textBox.fHandle) + +method `text=`(textBox: NativeTextBox, text: string) = + gtk_entry_set_text(textBox.fHandle, text) + app.processEvents() + +method naturalHeight(textBox: NativeTextBox): int = textBox.getTextLineHeight() + 12 # add padding + +method setSize(textBox: NativeTextBox, width, height: int) = + gtk_entry_set_width_chars(textBox.fHandle, 1) + procCall textBox.ControlImpl.setSize(width, height) + +method pAddButtonPressEvent(control: NativeTextBox) = + gtk_widget_add_events(control.fHandle, GDK_BUTTON_PRESS_MASK) + discard g_signal_connect_data(control.fHandle, "button-press-event", pDefaultControlButtonPressSignal, cast[pointer](control)) + + +# ---------------------------------------------------------------------------------------- +# TextArea +# ---------------------------------------------------------------------------------------- + +proc init(textArea: NativeTextArea) = + textArea.fHandle = gtk_scrolled_window_new(nil, nil) + # gtk_scrolled_window_set_policy(textArea.fHandle, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC) + gtk_scrolled_window_set_policy(textArea.fHandle, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC) + textArea.fTextViewHandle = gtk_text_view_new() + gtk_text_view_set_left_margin(textArea.fTextViewHandle, 5) + gtk_text_view_set_right_margin(textArea.fTextViewHandle, 5) + gtk_text_view_set_top_margin(textArea.fTextViewHandle, 5) + gtk_text_view_set_bottom_margin(textArea.fTextViewHandle, 5) + gtk_container_add(textArea.fHandle, textArea.fTextViewHandle) + gtk_widget_show(textArea.fTextViewHandle) + discard g_signal_connect_data(textArea.fTextViewHandle, "key-press-event", pControlKeyPressSignal, cast[pointer](textArea)) + textArea.fBufferHandle = gtk_text_view_get_buffer(textArea.fTextViewHandle) + discard g_signal_connect_data(textArea.fBufferHandle, "changed", pControlChangedSignal, cast[pointer](textArea)) + textArea.TextArea.init() + +method text(textArea: NativeTextArea): string = + var startIter, endIter: GtkTextIter + gtk_text_buffer_get_start_iter(textArea.fBufferHandle, startIter) + gtk_text_buffer_get_end_iter(textArea.fBufferHandle, endIter) + result = $gtk_text_buffer_get_text(textArea.fBufferHandle, startIter, endIter, 1) + +method `text=`(textArea: NativeTextArea, text: string) = + gtk_text_buffer_set_text(textArea.fBufferHandle, text, text.len.cint) + app.processEvents() + +method addText(textArea: NativeTextArea, text: string) = + # overide base method for better performance and to prevent automatic scrolling to top + var iter: GtkTextIter + gtk_text_buffer_get_end_iter(textArea.fBufferHandle, iter) + gtk_text_buffer_insert(textArea.fBufferHandle, iter, text, text.len.cint) + app.processEvents() + +method scrollToBottom(textArea: NativeTextArea) = + var iter: GtkTextIter + gtk_text_buffer_get_end_iter(textArea.fBufferHandle, iter) + gtk_text_view_scroll_to_iter(textArea.fTextViewHandle, iter, 0, false, 0, 0) + app.processEvents() + +method pAddButtonPressEvent(control: NativeTextArea) = + gtk_widget_add_events(control.fHandle, GDK_BUTTON_PRESS_MASK) + discard g_signal_connect_data(control.fHandle, "button-press-event", pDefaultControlButtonPressSignal, cast[pointer](control)) + +method `wrap=`(textArea: NativeTextArea, wrap: bool) = + procCall textArea.TextArea.`wrap=`(wrap) + if wrap: + gtk_text_view_set_wrap_mode(textArea.fTextViewHandle, GTK_WRAP_WORD_CHAR) + else: + gtk_text_view_set_wrap_mode(textArea.fTextViewHandle, GTK_WRAP_NONE) + diff --git a/src/nigui/private/gtk3/platform_types1.nim b/src/nigui/private/gtk3/platform_types1.nim new file mode 100755 index 0000000..d0178cd --- /dev/null +++ b/src/nigui/private/gtk3/platform_types1.nim @@ -0,0 +1,25 @@ +# NiGui - GTK+ 3 platform-specific code - part 1 + +# This file will be included in "nigui.nim". + +type + WindowImpl* = ref object of Window + fHandle: pointer + fInnerHandle: pointer + + ControlImpl* = ref object of Control + fHandle: pointer + fHScrollbar: pointer + fVScrollbar: pointer + fHAdjust: pointer + fVAdjust: pointer + fDeadCornerHandle: pointer + + CanvasImpl* = ref object of Canvas + fSurface: pointer + fData: cstring + fStride: int + fCairoContext: pointer + fFont: pointer + + ImageImpl* = ref object of Image diff --git a/src/nigui/private/gtk3/platform_types2.nim b/src/nigui/private/gtk3/platform_types2.nim new file mode 100755 index 0000000..c176ffc --- /dev/null +++ b/src/nigui/private/gtk3/platform_types2.nim @@ -0,0 +1,20 @@ +# NiGui - GTK+ 3 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 + fTextViewHandle: pointer + fBufferHandle: pointer 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.} + diff --git a/src/windows/nigui_platform_impl.nim b/src/windows/nigui_platform_impl.nim deleted file mode 100755 index c4c3010..0000000 --- a/src/windows/nigui_platform_impl.nim +++ /dev/null @@ -1,1278 +0,0 @@ -# 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/windows/nigui_platform_types1.nim b/src/windows/nigui_platform_types1.nim deleted file mode 100755 index ddf36cf..0000000 --- a/src/windows/nigui_platform_types1.nim +++ /dev/null @@ -1,23 +0,0 @@ -# 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/windows/nigui_platform_types2.nim b/src/windows/nigui_platform_types2.nim deleted file mode 100755 index bbe7caf..0000000 --- a/src/windows/nigui_platform_types2.nim +++ /dev/null @@ -1,18 +0,0 @@ -# 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/windows/windows.nim b/src/windows/windows.nim deleted file mode 100755 index 543985a..0000000 --- a/src/windows/windows.nim +++ /dev/null @@ -1,452 +0,0 @@ -# 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.} - -- cgit v1.2.3