summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/nigui.nim203
-rwxr-xr-xsrc/nigui/private/gtk3/gtk3.nim16
-rwxr-xr-xsrc/nigui/private/gtk3/platform_impl.nim173
-rwxr-xr-xsrc/nigui/private/gtk3/platform_types2.nim4
-rwxr-xr-xsrc/nigui/private/gtk3/platform_types3.nim8
-rwxr-xr-xsrc/nigui/private/windows/platform_impl.nim173
-rwxr-xr-xsrc/nigui/private/windows/platform_types2.nim2
-rwxr-xr-xsrc/nigui/private/windows/platform_types3.nim6
-rwxr-xr-xsrc/nigui/private/windows/windows.nim5
9 files changed, 342 insertions, 248 deletions
diff --git a/src/nigui.nim b/src/nigui.nim
index 6065cba..a6b3b0a 100755
--- a/src/nigui.nim
+++ b/src/nigui.nim
@@ -67,50 +67,61 @@ type
Timer* = distinct int
Key* = enum
- Key_None
- Key_Number0
- Key_Number1
- Key_Number2
- Key_Number3
- Key_Number4
- Key_Number5
- Key_Number6
- Key_Number7
- Key_Number8
- Key_Number9
- Key_A
- Key_B
- Key_C
- Key_D
- Key_E
- Key_F
- Key_G
- Key_H
- Key_I
- Key_J
- Key_K
- Key_L
- Key_M
- Key_N
- Key_O
- Key_P
- Key_Q
- Key_R
- Key_S
- Key_T
- Key_U
- Key_V
- Key_W
- Key_X
- Key_Y
- Key_Z
- Key_Space
- Key_Tab
- Key_Return
- Key_Escape
- Key_Insert
+ # Keys with same value than Unicode:
+ Key_None = 0
+ Key_Backspace = 8
+ Key_Tab = 9
+ Key_Return = 13
+ Key_Escape = 27
+ Key_Space = 32
+ Key_Asterisk = 42
+ Key_Plus = 43
+ Key_Comma = 44
+ Key_Minus = 45
+ Key_Point = 46
+ Key_Number0 = 48
+ Key_Number1 = 49
+ Key_Number2 = 50
+ Key_Number3 = 51
+ Key_Number4 = 52
+ Key_Number5 = 53
+ Key_Number6 = 54
+ Key_Number7 = 55
+ Key_Number8 = 56
+ Key_Number9 = 57
+ Key_A = 65
+ Key_B = 66
+ Key_C = 67
+ Key_D = 68
+ Key_E = 69
+ Key_F = 70
+ Key_G = 71
+ Key_H = 72
+ Key_I = 73
+ Key_J = 74
+ Key_K = 75
+ Key_L = 76
+ Key_M = 77
+ Key_N = 78
+ Key_O = 79
+ Key_P = 80
+ Key_Q = 81
+ Key_R = 82
+ Key_S = 83
+ Key_T = 84
+ Key_U = 85
+ Key_V = 86
+ Key_W = 87
+ Key_X = 88
+ Key_Y = 89
+ Key_Z = 90
+ Key_AE = 196
+ Key_OE = 214
+ Key_UE = 220
+ Key_SharpS = 223
+ # Not part of Unicode:
+ Key_Insert = 1000
Key_Delete
- Key_Backspace
Key_Left
Key_Right
Key_Up
@@ -120,7 +131,8 @@ type
Key_PageUp
Key_PageDown
-const inactiveTimer* = 0
+const
+ inactiveTimer* = 0
# ----------------------------------------------------------------------------------------
@@ -281,17 +293,15 @@ type
Button* = ref object of ControlImpl
fText: string
+ fEnabled: bool
Label* = ref object of ControlImpl
fText: string
TextBox* = ref object of ControlImpl
+ fEditable: bool
- TextArea* = ref object of ControlImpl
- fWrap: bool
-
-
-# Platform-specific extension of basic controls:
+# Platform-specific extension:
when useWindows(): include "nigui/private/windows/platform_types2"
when useGtk(): include "nigui/private/gtk3/platform_types2"
@@ -308,6 +318,13 @@ type
fPadding: int
fSpacing: int
+ TextArea* = ref object of NativeTextBox
+ fWrap: bool
+
+# Platform-specific extension:
+when useWindows(): include "nigui/private/windows/platform_types3"
+when useGtk(): include "nigui/private/gtk3/platform_types3"
+
# ----------------------------------------------------------------------------------------
# Global Variables
@@ -766,6 +783,9 @@ proc init*(button: NativeButton)
method text*(button: Button): string
method `text=`*(button: Button, text: string)
+method enabled*(button: Button): bool
+method `enabled=`*(button: Button, enabled: bool)
+
# ----------------------------------------------------------------------------------------
# Label
@@ -792,6 +812,21 @@ proc init*(textBox: NativeTextBox)
method text*(textBox: TextBox): string
method `text=`*(textBox: TextBox, text: string)
+method editable*(textBox: TextBox): bool
+method `editable=`*(textBox: TextBox, editable: bool)
+
+method cursorPos*(textBox: TextBox): int
+method `cursorPos=`*(textBox: TextBox, cursorPos: int)
+
+method selectionStart*(textBox: TextBox): int
+method `selectionStart=`*(textBox: TextBox, selectionStart: int)
+
+method selectionEnd*(textBox: TextBox): int
+method `selectionEnd=`*(textBox: TextBox, selectionEnd: int)
+
+method selectedText*(textBox: TextBox): string
+method `selectedText=`*(textBox: TextBox, text: string)
+
# ----------------------------------------------------------------------------------------
# TextArea
@@ -802,8 +837,6 @@ proc newTextArea*(text = ""): TextArea
proc init*(textArea: TextArea)
proc init*(textArea: NativeTextArea)
-method text*(textArea: TextArea): string
-method `text=`*(textArea: TextArea, text: string)
method addText*(textArea: TextArea, text: string)
method addLine*(textArea: TextArea, text = "")
@@ -892,7 +925,27 @@ proc rgb(red, green, blue: byte, alpha: byte = 255): Color =
result.blue = blue
result.alpha = alpha
-proc countLines(s: string): int = strutils.countLines(s) + 1
+# Should removed here, when version in strutils is fixed
+proc countLines(s: string): int =
+ result = 1
+ var i = 0
+ while i < s.len:
+ case s[i]
+ of '\c':
+ if s[i+1] == '\l': inc i
+ inc result
+ of '\l': inc result
+ else: discard
+ inc i
+
+proc unicodeToUpper(unicode: int): int =
+ if unicode < 128:
+ return cast[int](cast[char](unicode).toUpper)
+ result = case unicode:
+ of 228: 196 # Ä
+ of 246: 214 # Ö
+ of 252: 220 # Ü
+ else: unicode
proc sleep(app: App, milliSeconds: float) =
let t = epochTime() + milliSeconds / 1000
@@ -2136,6 +2189,7 @@ proc init(button: Button) =
button.fHeightMode = HeightMode_Auto
button.minWidth = 15
button.minHeight = 15
+ button.enabled = true
method text(button: Button): string = button.fText
@@ -2149,6 +2203,11 @@ method naturalWidth(button: Button): int = button.getTextWidth(button.text) + 20
method naturalHeight(button: Button): int = button.getTextLineHeight() * button.text.countLines + 12
+method enabled(button: Button): bool = button.fEnabled
+
+method `enabled=`(button: Button, enabled: bool) = discard
+ # has to be implemented by NativeTextBox
+
method `onDraw=`(container: NativeButton, callback: DrawProc) = raiseError("NativeButton does not allow onDraw.")
@@ -2198,6 +2257,7 @@ proc init(textBox: TextBox) =
textBox.fHeightMode = HeightMode_Auto
textBox.minWidth = 20
textBox.minHeight = 20
+ textBox.editable = true
method naturalHeight(textBox: TextBox): int = textBox.getTextLineHeight()
@@ -2209,6 +2269,38 @@ method `text=`(textBox: TextBox, text: string) = discard
method `onDraw=`(container: NativeTextBox, callback: DrawProc) = raiseError("NativeTextBox does not allow onDraw.")
+method editable(textBox: TextBox): bool = textBox.fEditable
+
+method `editable=`(textBox: TextBox, editable: bool) = discard
+ # has to be implemented by NativeTextBox
+
+method cursorPos(textBox: TextBox): int = discard
+ # has to be implemented by NativeTextBox
+
+method `cursorPos=`(textBox: TextBox, cursorPos: int) = discard
+ # has to be implemented by NativeTextBox
+
+method selectionStart(textBox: TextBox): int = discard
+ # has to be implemented by NativeTextBox
+
+method `selectionStart=`(textBox: TextBox, selectionStart: int) = discard
+ # has to be implemented by NativeTextBox
+
+method selectionEnd(textBox: TextBox): int = discard
+ # has to be implemented by NativeTextBox
+
+method `selectionEnd=`(textBox: TextBox, selectionEnd: int) = discard
+ # has to be implemented by NativeTextBox
+
+method selectedText(textBox: TextBox): string =
+ result = textBox.text.substr(textBox.selectionStart, textBox.selectionEnd - 1)
+
+method `selectedText=`(textBox: TextBox, text: string) =
+ let oldCursorPos = textBox.cursorPos
+ let oldText = textBox.text
+ textBox.text = oldText.substr(0, textBox.selectionStart - 1) & text & oldText.substr(textBox.selectionEnd)
+ textBox.cursorPos = oldCursorPos
+
# ----------------------------------------------------------------------------------------
# TextArea
@@ -2226,12 +2318,7 @@ proc init(textArea: TextArea) =
textArea.minWidth = 20
textArea.minHeight = 20
textArea.wrap = true
-
-method text(textArea: TextArea): string = discard
- # has to be implemented by NativeTextBox
-
-method `text=`(textArea: TextArea, text: string) = discard
- # has to be implemented by NativeTextBox
+ textArea.editable = true
method addText(textArea: TextArea, text: string) = textArea.text = textArea.text & text
diff --git a/src/nigui/private/gtk3/gtk3.nim b/src/nigui/private/gtk3/gtk3.nim
index eff2a94..3e83f62 100755
--- a/src/nigui/private/gtk3/gtk3.nim
+++ b/src/nigui/private/gtk3/gtk3.nim
@@ -245,6 +245,7 @@ proc gtk_widget_grab_focus*(widget: pointer) {.importc: "gtk_widget_grab_focus",
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_widget_set_sensitive*(widget: pointer, sensitive: bool) {.importc: "gtk_widget_set_sensitive", 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.}
@@ -303,6 +304,10 @@ proc gtk_entry_get_text*(entry: pointer): cstring {.importc: "gtk_entry_get_text
proc gtk_entry_set_width_chars*(entry: pointer, n_chars: cint) {.importc: "gtk_entry_set_width_chars", libgtk3.}
proc gtk_editable_get_selection_bounds*(editable: pointer, start_pos, end_pos: var cint): bool {.importc: "gtk_editable_get_selection_bounds", libgtk3.}
proc gtk_editable_get_chars*(editable: pointer, start_pos, end_pos: cint): cstring {.importc: "gtk_editable_get_chars", libgtk3.}
+proc gtk_editable_select_region*(editable: pointer, start_pos, end_pos: cint) {.importc: "gtk_editable_select_region", libgtk3.}
+proc gtk_editable_get_position*(editable: pointer): cint {.importc: "gtk_editable_get_position", libgtk3.}
+proc gtk_editable_set_position*(editable: pointer, position: cint) {.importc: "gtk_editable_set_position", libgtk3.}
+proc gtk_editable_set_editable*(editable: pointer, is_editable: bool) {.importc: "gtk_editable_set_editable", 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.}
@@ -315,8 +320,7 @@ proc gtk_text_view_set_bottom_margin*(text_view: pointer, margin: cint) {.import
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_view_set_editable*(text_view: pointer, setting: bool) {.importc: "gtk_text_view_set_editable", 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.}
@@ -324,10 +328,16 @@ proc gtk_text_buffer_get_text*(text_buffer: pointer, start, `end`: var GtkTextIt
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_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_text_buffer_get_selection_bounds*(buffer: pointer, start, `end`: var GtkTextIter): bool {.importc: "gtk_text_buffer_get_selection_bounds", libgtk3.}
+proc gtk_text_buffer_select_range*(buffer: pointer, ins, bound: var GtkTextIter) {.importc: "gtk_text_buffer_select_range", libgtk3.}
+proc gtk_text_buffer_get_iter_at_offset*(buffer: pointer, iter: var GtkTextIter, char_offset: cint) {.importc: "gtk_text_buffer_get_iter_at_offset", libgtk3.}
+proc gtk_text_buffer_get_iter_at_mark*(buffer: pointer, iter: var GtkTextIter, mark: pointer) {.importc: "gtk_text_buffer_get_iter_at_mark", libgtk3.}
+
+proc gtk_text_iter_get_offset*(iter: var GtkTextIter): cint {.importc: "gtk_text_iter_get_offset", libgtk3.}
+# proc gtk_text_mark_new*(name: cstring, left_gravity: bool): pointer {.importc: "gtk_text_mark_new", 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.}
diff --git a/src/nigui/private/gtk3/platform_impl.nim b/src/nigui/private/gtk3/platform_impl.nim
index 405a3a3..508373c 100755
--- a/src/nigui/private/gtk3/platform_impl.nim
+++ b/src/nigui/private/gtk3/platform_impl.nim
@@ -61,70 +61,7 @@ proc pWindowConfigureSignal(windowHandle, event, data: pointer): bool {.cdecl.}
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
+ result = case keyval
of 65289: Key_Tab
of 65293: Key_Return
of 65307: Key_Escape
@@ -139,7 +76,7 @@ proc pKeyvalToKey(keyval: cint): Key =
of 65367: Key_End
of 65365: Key_PageUp
of 65366: Key_PageDown
- else: Key_None
+ else: cast[Key](keyval.unicodeToUpper)
proc pWindowKeyPressSignal(widget: pointer, event: var GdkEventKey, data: pointer): bool {.cdecl.} =
# echo "window keyPressCallback"
@@ -1092,6 +1029,10 @@ 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))
+method `enabled=`(button: NativeButton, enabled: bool) =
+ button.fEnabled = enabled
+ gtk_widget_set_sensitive(button.fHandle, enabled)
+
# ----------------------------------------------------------------------------------------
# Label
@@ -1129,12 +1070,8 @@ proc pTextBoxKeyPressSignal(widget: pointer, event: var GdkEventKey, data: point
let modifiers = gtk_accelerator_get_default_mod_mask()
if event.keyval == 'c'.ord and (event.state and modifiers) == GDK_CONTROL_MASK:
let textBox = cast[NativeTextBox](data)
- var startPos: cint
- var endPos: cint
- discard gtk_editable_get_selection_bounds(textBox.fHandle, startPos, endPos)
- if startPos != endPos:
- app.clipboardText = $gtk_editable_get_chars(textBox.fHandle, startPos, endPos)
- return true # prevent default "copy to clipboard"
+ app.clipboardText = textBox.selectedText
+ return true # prevent default "copy to clipboard"
proc init(textBox: NativeTextBox) =
textBox.fHandle = gtk_entry_new()
@@ -1158,25 +1095,42 @@ 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))
+method `editable=`(textBox: NativeTextBox, editable: bool) =
+ textBox.fEditable = editable
+ gtk_editable_set_editable(textBox.fHandle, editable)
+
+method cursorPos(textBox: NativeTextBox): int =
+ result = gtk_editable_get_position(textBox.fHandle)
+
+method `cursorPos=`(textBox: NativeTextBox, cursorPos: int) =
+ # side effect: clears selection
+ gtk_editable_set_position(textBox.fHandle, cursorPos.cint)
+
+method selectionStart(textBox: NativeTextBox): int =
+ var startPos: cint
+ var endPos: cint
+ discard gtk_editable_get_selection_bounds(textBox.fHandle, startPos, endPos)
+ result = startPos
+
+method selectionEnd(textBox: NativeTextBox): int =
+ var startPos: cint
+ var endPos: cint
+ discard gtk_editable_get_selection_bounds(textBox.fHandle, startPos, endPos)
+ result = endPos
+
+method `selectionStart=`(textBox: NativeTextBox, selectionStart: int) =
+ gtk_editable_select_region(textBox.fHandle, selectionStart.cint, textBox.selectionEnd.cint)
+ # side effect: sets cursor to end of selection
+
+method `selectionEnd=`(textBox: NativeTextBox, selectionEnd: int) =
+ gtk_editable_select_region(textBox.fHandle, textBox.selectionStart.cint, selectionEnd.cint)
+ # side effect: sets cursor to end of selection
+
# ----------------------------------------------------------------------------------------
# TextArea
# ----------------------------------------------------------------------------------------
-proc pTextAreaKeyPressSignal(widget: pointer, event: var GdkEventKey, data: pointer): bool {.cdecl.} =
- result = pControlKeyPressSignal(widget, event, data)
-
- # Implement own "copy to clipboard", because by default the clipboard is non-persistent
- if not result:
- let modifiers = gtk_accelerator_get_default_mod_mask()
- if event.keyval == 'c'.ord and (event.state and modifiers) == GDK_CONTROL_MASK:
- let textArea = cast[NativeTextArea](data)
- var startIter: GtkTextIter
- var endIter: GtkTextIter
- discard gtk_text_buffer_get_selection_bounds(textArea.fBufferHandle, startIter, endIter)
- app.clipboardText = $gtk_text_buffer_get_text(textArea.fBufferHandle, startIter, endIter, true)
- return true # prevent default "copy to clipboard"
-
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)
@@ -1188,11 +1142,15 @@ proc init(textArea: NativeTextArea) =
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", pTextAreaKeyPressSignal, cast[pointer](textArea))
+ discard g_signal_connect_data(textArea.fTextViewHandle, "key-press-event", pTextBoxKeyPressSignal, 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 setSize(textBox: NativeTextArea, width, height: int) =
+ # Need to override method of NativeTextBox
+ procCall textBox.ControlImpl.setSize(width, height)
+
method text(textArea: NativeTextArea): string =
var startIter, endIter: GtkTextIter
gtk_text_buffer_get_start_iter(textArea.fBufferHandle, startIter)
@@ -1227,3 +1185,46 @@ method `wrap=`(textArea: NativeTextArea, wrap: bool) =
else:
gtk_text_view_set_wrap_mode(textArea.fTextViewHandle, GTK_WRAP_NONE)
+method `editable=`(textArea: NativeTextArea, editable: bool) =
+ textArea.fEditable = editable
+ gtk_text_view_set_editable(textArea.fTextViewHandle, editable)
+
+method cursorPos(textArea: NativeTextArea): int =
+ let mark = gtk_text_buffer_get_insert(textArea.fBufferHandle)
+ var iter: GtkTextIter
+ gtk_text_buffer_get_iter_at_mark(textArea.fBufferHandle, iter, mark)
+ result = gtk_text_iter_get_offset(iter)
+
+method `cursorPos=`(textArea: NativeTextArea, cursorPos: int) =
+ # side effect: clears selection
+ var iter: GtkTextIter
+ gtk_text_buffer_get_iter_at_offset(textArea.fBufferHandle, iter, cursorPos.cint)
+ gtk_text_buffer_select_range(textArea.fBufferHandle, iter, iter)
+
+method selectionStart(textArea: NativeTextArea): int =
+ var startIter: GtkTextIter
+ var endIter: GtkTextIter
+ discard gtk_text_buffer_get_selection_bounds(textArea.fBufferHandle, startIter, endIter)
+ result = gtk_text_iter_get_offset(startIter)
+
+method selectionEnd(textArea: NativeTextArea): int =
+ var startIter: GtkTextIter
+ var endIter: GtkTextIter
+ discard gtk_text_buffer_get_selection_bounds(textArea.fBufferHandle, startIter, endIter)
+ result = gtk_text_iter_get_offset(endIter)
+
+method `selectionStart=`(textArea: NativeTextArea, selectionStart: int) =
+ var startIter: GtkTextIter
+ var endIter: GtkTextIter
+ gtk_text_buffer_get_iter_at_offset(textArea.fBufferHandle, startIter, selectionStart.cint)
+ gtk_text_buffer_get_iter_at_offset(textArea.fBufferHandle, endIter, textArea.selectionEnd.cint)
+ gtk_text_buffer_select_range(textArea.fBufferHandle, startIter, endIter)
+ # side effect: sets cursor to start of selection
+
+method `selectionEnd=`(textArea: NativeTextArea, selectionEnd: int) =
+ var startIter: GtkTextIter
+ var endIter: GtkTextIter
+ gtk_text_buffer_get_iter_at_offset(textArea.fBufferHandle, startIter, textArea.selectionStart.cint)
+ gtk_text_buffer_get_iter_at_offset(textArea.fBufferHandle, endIter, selectionEnd.cint)
+ gtk_text_buffer_select_range(textArea.fBufferHandle, startIter, endIter)
+ # side effect: sets cursor to start of selection
diff --git a/src/nigui/private/gtk3/platform_types2.nim b/src/nigui/private/gtk3/platform_types2.nim
index c176ffc..5faa8b6 100755
--- a/src/nigui/private/gtk3/platform_types2.nim
+++ b/src/nigui/private/gtk3/platform_types2.nim
@@ -14,7 +14,3 @@ type
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/gtk3/platform_types3.nim b/src/nigui/private/gtk3/platform_types3.nim
new file mode 100755
index 0000000..d424f3b
--- /dev/null
+++ b/src/nigui/private/gtk3/platform_types3.nim
@@ -0,0 +1,8 @@
+# NiGui - GTK+ 3 platform-specific code - part 3
+
+# This file will be included in "nigui.nim".
+
+type
+ 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
index 8013706..2466e00 100755
--- a/src/nigui/private/windows/platform_impl.nim
+++ b/src/nigui/private/windows/platform_impl.nim
@@ -207,51 +207,8 @@ proc pCommonWndProc(hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointe
discard
result = DefWindowProcA(hWnd, uMsg, wParam, lParam)
-proc pKeyvalToKey(keyval: int): Key =
+proc pVirtualKeyToKey(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
@@ -260,32 +217,20 @@ proc pKeyvalToKey(keyval: int): Key =
of 36: Key_Home
of 33: Key_PageUp
of 34: Key_PageDown
- else: Key_None
+ else: cast[Key](keyval.unicodeToUpper)
-proc pHandleWMKEYDOWN(window: Window, control: Control, wParam, lParam: pointer) =
+proc pHandleWMKEYDOWNOrWMCHAR(window: Window, control: Control, unicode: int, key: Key): bool =
var windowEvent = new WindowKeyEvent
windowEvent.window = window
- windowEvent.key = pKeyvalToKey(cast[int](wParam))
+ windowEvent.key = key
if windowEvent.key == Key_None:
- echo "Unkown key value: ", cast[int](wParam)
+ echo "WM_CHAR: Unkown key value: ", unicode
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)
+ windowEvent.unicode = unicode
+ windowEvent.character = unicode.pUnicodeCharToUtf8
- # var windowEvent = new WindowKeyEvent
- # windowEvent.window = window
- # windowEvent.character = $chr(cast[int](wParam))
- # windowEvent.key = pKeyvalToKey(cast[int](wParam))
- # window.handleKeyDownEvent(windowEvent)
+ window.handleKeyDownEvent(windowEvent)
if control != nil:
var controlEvent = new ControlKeyEvent
@@ -294,9 +239,20 @@ proc pHandleWMKEYDOWN(window: Window, control: Control, wParam, lParam: pointer)
controlEvent.unicode = windowEvent.unicode
controlEvent.character = windowEvent.character
control.handleKeyDownEvent(controlEvent)
- # if controlEvent.cancel:
- # return nil # key is still inserted in text area
+ result = controlEvent.cancel
+
+proc pHandleWMKEYDOWN(window: Window, control: Control, wParam, lParam: pointer): bool =
+ 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:
+ return # Unicode characters are handled by WM_CHAR
+ result = pHandleWMKEYDOWNOrWMCHAR(window, control, 0, pVirtualKeyToKey(cast[int](wParam)))
+proc pHandleWMCHAR(window: Window, control: Control, wParam, lParam: pointer): bool =
+ let unicode = cast[int](wParam)
+ result = pHandleWMKEYDOWNOrWMCHAR(window, control, unicode, cast[Key](unicode.unicodeToUpper))
proc pWindowWndProc(hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointer {.cdecl.} =
case uMsg
@@ -339,31 +295,14 @@ proc pWindowWndProc(hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointe
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](pGetWindowLongPtr(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](pGetWindowLongPtr(hWnd, GWLP_USERDATA))
- if window != nil:
- pHandleWMKEYDOWN(window, nil, wParam, lParam)
+ if window != nil and pHandleWMKEYDOWN(window, nil, wParam, lParam):
+ return
+ of WM_CHAR:
+ let window = cast[Window](pGetWindowLongPtr(hWnd, GWLP_USERDATA))
+ if window != nil and pHandleWMCHAR(window, nil, wParam, lParam):
+ return
else:
discard
result = pCommonWndProc(hWnd, uMsg, wParam, lParam)
@@ -427,7 +366,7 @@ proc clipboardText(app: App): string =
result = $text
discard GlobalUnlock(data)
discard CloseClipboard()
-
+
proc `clipboardText=`(app: App, text: string) =
if not OpenClipboard(nil):
return
@@ -1032,11 +971,21 @@ proc pCommonControlWndProc_Scroll(origWndProc, hWnd: pointer, uMsg: int32, wPara
proc pCommonControlWndProc(origWndProc, hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointer =
case uMsg
+
+ # Note: A WM_KEYDOWN is sent for every key, for some (mostly visual) keys WM_CHAR is sent in addition.
+ # To discard a character in text input, WM_CHAR must return without calling the default window proc.
+ # Because we should not to trigger two events for one key press, WM_KEYDOWN must ignore all keys,
+ # which are handled by WM_CHAR.
+
of WM_KEYDOWN:
let control = cast[Control](pGetWindowLongPtr(hWnd, GWLP_USERDATA))
- if control != nil:
- # echo "control WM_KEYDOWN"
- pHandleWMKEYDOWN(control.parentWindow, control, wParam, lParam)
+ if control != nil and pHandleWMKEYDOWN(control.parentWindow, control, wParam, lParam):
+ return nil
+
+ of WM_CHAR:
+ let control = cast[Control](pGetWindowLongPtr(hWnd, GWLP_USERDATA))
+ if control != nil and pHandleWMCHAR(control.parentWindow, control, wParam, lParam):
+ return nil
# of WM_KEYUP:
# return nil # key is still inserted in text area
@@ -1245,6 +1194,10 @@ method `text=`(button: NativeButton, text: string) =
procCall button.Button.`text=`(text)
pSetWindowText(button.fHandle, text)
+method `enabled=`(button: NativeButton, enabled: bool) =
+ button.fEnabled = enabled
+ discard EnableWindow(button.fHandle, enabled)
+
# ----------------------------------------------------------------------------------------
# Label
@@ -1279,6 +1232,36 @@ method `text=`(textBox: NativeTextBox, text: string) = pSetWindowText(textBox.fH
method naturalHeight(textBox: NativeTextBox): int = textBox.getTextLineHeight() + 9 # add padding
+method `editable=`(textBox: NativeTextBox, editable: bool) =
+ textBox.fEditable = editable
+ discard SendMessageA(textBox.fHandle, EM_SETREADONLY, cast[pointer](not editable), nil)
+
+method cursorPos(textBox: NativeTextBox): int =
+ var startPos: int32
+ discard SendMessageA(textBox.fHandle, EM_GETSEL, startPos.addr, nil)
+ result = startPos
+ # Not really the cursor position, but the start of selection
+
+method `cursorPos=`(textBox: NativeTextBox, cursorPos: int) =
+ discard SendMessageA(textBox.fHandle, EM_SETSEL, cast[pointer](cursorPos), cast[pointer](cursorPos))
+ # Side effect: clears selection
+
+method selectionStart(textBox: NativeTextBox): int =
+ var startPos: int32
+ discard SendMessageA(textBox.fHandle, EM_GETSEL, startPos.addr, nil)
+ result = startPos
+
+method selectionEnd(textBox: NativeTextBox): int =
+ var endPos: int32
+ discard SendMessageA(textBox.fHandle, EM_GETSEL, nil, endPos.addr)
+ result = endPos
+
+method `selectionStart=`(textBox: NativeTextBox, selectionStart: int) =
+ discard SendMessageA(textBox.fHandle, EM_SETSEL, cast[pointer](selectionStart), cast[pointer](textBox.selectionEnd))
+
+method `selectionEnd=`(textBox: NativeTextBox, selectionEnd: int) =
+ discard SendMessageA(textBox.fHandle, EM_SETSEL, cast[pointer](textBox.selectionStart), cast[pointer](selectionEnd))
+
# ----------------------------------------------------------------------------------------
# TextArea
@@ -1302,9 +1285,11 @@ proc init(textArea: NativeTextArea) =
pTextAreaOrigWndProc = pSetWindowLongPtr(textArea.fHandle, GWLP_WNDPROC, pTextAreaWndProc)
textArea.TextArea.init()
-method text(textArea: NativeTextArea): string = pGetWindowText(textArea.fHandle)
+# method text(textArea: NativeTextArea): string = pGetWindowText(textArea.fHandle)
+# not needed any more
-method `text=`(textArea: NativeTextArea, text: string) = pSetWindowText(textArea.fHandle, text)
+# method `text=`(textArea: NativeTextArea, text: string) = pSetWindowText(textArea.fHandle, text)
+# not needed any more
method scrollToBottom(textArea: NativeTextArea) =
# select all
diff --git a/src/nigui/private/windows/platform_types2.nim b/src/nigui/private/windows/platform_types2.nim
index bbe7caf..120f15f 100755
--- a/src/nigui/private/windows/platform_types2.nim
+++ b/src/nigui/private/windows/platform_types2.nim
@@ -14,5 +14,3 @@ type
NativeLabel* = ref object of Label
NativeTextBox* = ref object of TextBox
-
- NativeTextArea* = ref object of TextArea
diff --git a/src/nigui/private/windows/platform_types3.nim b/src/nigui/private/windows/platform_types3.nim
new file mode 100755
index 0000000..7219027
--- /dev/null
+++ b/src/nigui/private/windows/platform_types3.nim
@@ -0,0 +1,6 @@
+# NiGui - Win32 platform-specific code - part 3
+
+# This file will be included in "nigui.nim".
+
+type
+ NativeTextArea* = ref object of TextArea
diff --git a/src/nigui/private/windows/windows.nim b/src/nigui/private/windows/windows.nim
index 7e3f959..536d345 100755
--- a/src/nigui/private/windows/windows.nim
+++ b/src/nigui/private/windows/windows.nim
@@ -46,7 +46,9 @@ const
CW_USEDEFAULT* = 0x80000000.int
DEFAULT_GUI_FONT* = 17
EM_SCROLLCARET* = 183
+ EM_GETSEL* = 176
EM_SETSEL* = 177
+ EM_SETREADONLY* = 207
EN_CHANGE* = 768
ES_MULTILINE* = 4
GCLP_HBRBACKGROUND* = -10
@@ -155,7 +157,7 @@ const
# UnitDocument* = 5
# UnitMillimeter* = 6
GMEM_MOVEABLE* = 2
-
+
# ----------------------------------------------------------------------------------------
# Types
@@ -383,6 +385,7 @@ proc CloseClipboard*(): bool {.importc: "CloseClipboard", libUser32.}
proc GetClipboardData*(uFormat: int32): pointer {.importc: "GetClipboardData", libUser32.}
proc SetClipboardData*(uFormat: int32, hMem: pointer): pointer {.importc: "SetClipboardData", libUser32.}
proc EmptyClipboard*(): bool {.importc: "EmptyClipboard", libUser32.}
+# proc MapVirtualKeyW*(uCode, uMapType: int32): int32 {.importc: "MapVirtualKeyW", libUser32.}
when defined(cpu64):
# Only available on 64-bit Windows: