summaryrefslogtreecommitdiff
path: root/src/nigui
diff options
context:
space:
mode:
authortrustable-code <krauter.simon@arcor.de>2017-07-06 21:55:17 +0200
committertrustable-code <krauter.simon@arcor.de>2017-07-06 21:55:17 +0200
commit911c4e13f379f887b89fd176e5878d4726484744 (patch)
treefb37cd7303da6ad53f4a08b254eedfb6893bdaa9 /src/nigui
parentf827a1ac3e3095aec40ca7c270e154bda45c2bf4 (diff)
downloadNiGui-911c4e13f379f887b89fd176e5878d4726484744.tar.gz
NiGui-911c4e13f379f887b89fd176e5878d4726484744.zip
Change directory structure, change include concept, add nimble file
Diffstat (limited to 'src/nigui')
-rwxr-xr-xsrc/nigui/private/gtk3/gtk3.nim430
-rwxr-xr-xsrc/nigui/private/gtk3/platform_impl.nim1176
-rwxr-xr-xsrc/nigui/private/gtk3/platform_types1.nim25
-rwxr-xr-xsrc/nigui/private/gtk3/platform_types2.nim20
-rwxr-xr-xsrc/nigui/private/windows/platform_impl.nim1278
-rwxr-xr-xsrc/nigui/private/windows/platform_types1.nim23
-rwxr-xr-xsrc/nigui/private/windows/platform_types2.nim18
-rwxr-xr-xsrc/nigui/private/windows/windows.nim452
8 files changed, 3422 insertions, 0 deletions
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.}
+