diff options
| author | trustable-code <krauter.simon@arcor.de> | 2017-07-06 21:55:17 +0200 |
|---|---|---|
| committer | trustable-code <krauter.simon@arcor.de> | 2017-07-06 21:55:17 +0200 |
| commit | 911c4e13f379f887b89fd176e5878d4726484744 (patch) | |
| tree | fb37cd7303da6ad53f4a08b254eedfb6893bdaa9 /src/nigui | |
| parent | f827a1ac3e3095aec40ca7c270e154bda45c2bf4 (diff) | |
| download | NiGui-911c4e13f379f887b89fd176e5878d4726484744.tar.gz NiGui-911c4e13f379f887b89fd176e5878d4726484744.zip | |
Change directory structure, change include concept, add nimble file
Diffstat (limited to 'src/nigui')
| -rwxr-xr-x | src/nigui/private/gtk3/gtk3.nim | 430 | ||||
| -rwxr-xr-x | src/nigui/private/gtk3/platform_impl.nim | 1176 | ||||
| -rwxr-xr-x | src/nigui/private/gtk3/platform_types1.nim | 25 | ||||
| -rwxr-xr-x | src/nigui/private/gtk3/platform_types2.nim | 20 | ||||
| -rwxr-xr-x | src/nigui/private/windows/platform_impl.nim | 1278 | ||||
| -rwxr-xr-x | src/nigui/private/windows/platform_types1.nim | 23 | ||||
| -rwxr-xr-x | src/nigui/private/windows/platform_types2.nim | 18 | ||||
| -rwxr-xr-x | src/nigui/private/windows/windows.nim | 452 |
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.} + |
