Files
kolibrios/programs/develop/libraries/box_lib/editbox.asm
T
2757ea7d3e
Build system / Build (en_US) (push) Successful in 3m35s
Build system / Build (es_ES) (push) Successful in 3m39s
Build system / Build (ru_RU) (push) Successful in 3m48s
Build system / Publish Images (push) Successful in 6m56s
libs/boxlib: rewrite editbox.asm (fix #652) (#662)
Rewrite of the editbox widget. Binary layout and public API unchanged.

- much more readable code
- scroll step = actual chars that fit in the field (`visibleCount`, ex-`shift_old`), not hardcoded 8 (fixes #652)
- `edit_box` formalized as a fasm `struct` with named fields; unions add readable aliases (`caretPos`, `textLen`, `selectColor`, ...) to the old names
- `txCol` -> `txColor` (`BColor` union with per-channel access)
- clipboard: payload described by `BClipBuf` struct, content validated before paste, fixed paste arithmetic and a memory leak on read
- restored Ctrl+C
- fixed `not cx` truncation (buffer overflow), stale redraw in insert mode, division with uninitialized edx
- control flow untangled into procedures, net -94 lines

---------

Co-authored-by: Burer <burer@kolibrios.org>
Co-authored-by: ZReticules <my_mailbox_228@mail.ru>
Reviewed-on: #662
Reviewed-by: IgorA <22+igora@noreply.localhost>
Reviewed-by: Mikhail Frolov <mixa.frolov2003@gmail.com>
Reviewed-by: Burer <burer@kolibrios.org>
Co-authored-by: The Bird of Hermes <zlo.shustrik@mail.ru>
2026-09-01 14:26:21 +00:00

1282 lines
24 KiB
FASM

SCAN_LWIN_RELEASE = 0xDB
SCAN_RWIN_RELEASE = 0xDC
struct EditBox.InplaceStr
len rd 1
p_str rd 1
ends
struct BClipBuf
cbSize rd 1
contentType rd 1
encoding rd 1
data_ rb 0
ends
struct BColor
union
bgrf rd 1
struct
b rb 1
g rb 1
r rb 1
format rb 1
ends
ends
ends
struct BPoint
x rw 1
y rw 1
ends
struct EditBox
width dd ?
left dd ?
top dd ?
union
color dd ?
bgColor dd ?
ends
union
shift_color dd ?
selectColor dd ?
ends
union
focus_border_color dd ?
focusBorderColor dd ?
ends
union
blur_border_color dd ?
blurBorderColor dd ?
ends
union
text_color dd ?
txColor BColor
ends
union
max dd ?
textMaxLen dd ?
ends
text dd ?
union
mouse_variable dd ?
pp_mouseOwner dd ?
ends
flags dd ?
union
size dd ?
textLen dd ?
ends
union
pos dd ?
caretPos dd ?
ends
offset dd 0
union
struct
cl_curs_x dw 0
cl_curs_y dw 0
ends
cursorPos BPoint
ends
union
shift dw 0
selectionPos dw ?
ends
union
shift_old dw 0
visibleCount dw ?
ends
height dd 0
union
char_width dd 0
charWidth dd ?
ends
ends
ES.PASSWORD = 1 ; режим для ввода пароля
ES.FOCUS = 2 ; в фокусе
ES.SELECT = 4 ; есть выбранный текст
ES.SHIFT_ON = 8 ; при вводе нажат шифт
ES.DRAWN_ONCE = 16 ; отрисован хоть раз (бывший ES.SELECT_BAC)
ES.LEFT_FL = 32 ; не используется
ES.OFFSET_FL = 64 ; не используется
ES.INSERT_MODE = 128 ; режим вода с перезаписью
ES.MOUSE_ON = 256 ; удерживает мышь
ES.CTRL_ON = 512 ; при вводе нажат ctrl
ES.ALT_ON = 1024 ; при вводе нажат alt
ES.DISABLED = 2048 ; отключен
ES.ALWAYS_FOCUS = 16384 ; всегда в фокусе, после получения не теряет
ES.NUMERIC = 32768 ; режим ввода только цифры
; in private methods edi always contains 'this' pointer
align 4
proc EditBox.__drawBorder
virtual at edi
.this EditBox
end virtual
mov edx, [.this.focusBorderColor]
test [.this.flags], ES.FOCUS
jne @f
mov edx, [.this.blurBorderColor]
@@:
; left:left
mov ebx, [.this.left]
mov eax, ebx
shl ebx, 16
or ebx, eax
; top:top
mov ecx, [.this.top]
mov eax, ecx
shl ecx, 16
or ecx, eax
mov esi, [.this.height]
inc esi
mov ebp, [.this.width]
add cx, si ; top:bottom
mov eax, SF_DRAW_LINE
mcall ; left top -> left bottom
rol ecx, 16
add cx, si ; bottom:bottom
add bx, bp ; left:right
mcall ; left bottom -> right bottom
rol ebx, 16
add bx, bp ; right:right
sub cx, si ; bottom:top
mcall ; right bottom -> right top
rol ecx, 16
sub cx, si ; top:top
sub bx, bp ; right:left
mcall ; right top -> left top
ret
endp
macro EditBox.__visibleSmbCount{
local ..zero_width
mov edx, [edi + EditBox.width]
xor eax, eax
cmp [edi + EditBox.charWidth], 0
je ..zero_width
lea eax, [edx - 4]
xor edx, edx
div [edi + EditBox.charWidth]
..zero_width:
}
align 4
proc EditBox.__drawBg
virtual at edi
.this EditBox
end virtual
mov ebx, [.this.left]
inc ebx
shl ebx, 16
mov bx, word[.this.width]
dec bx
mov edx, [.this.bgColor]
mov eax, 0xCACACA
test [.this.flags], ES.DISABLED
cmovnz edx, eax
mov ecx, [.this.top]
inc ecx
shl ecx, 16
mov cx, word[.this.height]
mcall SF_DRAW_RECT
ret
endp
;----------------------------------------------------------
; Calculate offset to visible part of the text based on
; current caret position
;----------------------------------------------------------
align 4
proc EditBox.__calcOffset
virtual at edi
.this EditBox
end virtual
movzx eax, [.this.visibleCount]
mov ecx, [.this.caretPos]
mov edx, [.this.offset]
cmp [.this.offset], ecx
ja .offset_above
lea edx, [edx + eax + 1] ; + 1 for normal cursor position on the edge of the left
cmp edx, [.this.caretPos]
ja .end_
; check if text len is equal to caret position
mov edx, [.this.textLen]
cmp edx, [.this.caretPos]
jne @f
xor ebx, ebx
sub edx, eax
cmovs edx, ebx
mov [.this.offset], edx
jmp .end_
@@:
; offset increment correction
; by default visible contents shifts by 8
; but if visible content is lower than that,
; we shift by the size of visible content size
mov ebx, 8
cmp eax, ebx
cmova eax, ebx
add [.this.offset], eax
; check if we are still too far from caret
; in that case, set offset to caretPos - visibleCount
mov ebx, [.this.offset]
add bx, [.this.visibleCount]
cmp ebx, [.this.caretPos]
ja @f
mov ebx, ecx
sub bx, [.this.visibleCount]
mov [.this.offset], ebx
@@:
jmp .end_
.offset_above:
; offset decrement correction
; by default visible contents shifts by 8
; but if visible content is lower than that,
; it shifts by the size of visible content size
mov ebx, 8
cmp eax, ebx
cmova eax, ebx
; check if decreased value lower than 0
xor ebx, ebx
sub edx, eax
cmovb edx, ebx
; check if offset still above caret
mov ebx, ecx
cmp edx, ecx
jbe .offset_not_above_caret
; in case it is, set offset to caretPos - 1, if caretPos is higher than 0
test ebx, ebx
jz @f
dec ebx
@@:
mov edx, ebx
.offset_not_above_caret:
mov [.this.offset], edx
.end_:
; fix if offset shifts us from the end of visible field
; while we near to the text end
mov eax, [.this.offset]
mov edx, [.this.textLen]
movzx ebx, [.this.visibleCount]
sub edx, ebx
cmp ecx, edx
jb @f
; check if we are near the end, but end of text is above visible range
mov ecx, eax
add cx, [.this.visibleCount]
cmp ecx, [.this.textLen]
cmova eax, edx
@@:
mov [.this.offset], eax
ret
endp
align 4
proc EditBox.__drawText
virtual at edi
.this EditBox
end virtual
movzx eax, [.this.visibleCount]
mov esi, [.this.textLen]
sub esi, [.this.offset]
cmp eax, esi
cmovb esi, eax
test esi, esi
jz .no_text
mov eax, SF_DRAW_TEXT
mov ebx, [.this.left]
add ebx, 2
shl ebx, 16
add bx, word[.this.top]
add bx, 3
mov ecx, [.this.txColor.bgrf]
test [.this.flags], ES.PASSWORD
jnz .password_mode
mov edx, [.this.text]
add edx, [.this.offset]
mcall
ret
.password_mode:
local .charWidth:DWORD
mov edx, [.this.charWidth]
push edi
mov edi, esi
mov esi,1
shl edx, 16
mov [.charWidth], edx
mov edx, .txtPass
@@:
mcall
add ebx, [.charWidth]
dec edi
jnz @b
.no_text:
ret
.txtPass db "*"
endp
align 4
proc EditBox.__drawSelection
virtual at edi
.this EditBox
end virtual
test [.this.flags], ES.SELECT
jz .no_selection
movzx eax, [.this.visibleCount]
mov ecx, [.this.offset]
add eax, ecx
; ebx - min, esi- max
mov edx, [.this.caretPos]
mov ebx, edx
movzx esi, [.this.selectionPos]
cmp esi, ebx
jae @f
xchg esi, ebx
@@:
cmp eax, esi
cmovb esi, eax
cmp ebx, ecx
cmovb ebx, ecx
sub ebx, ecx
sub esi, ecx
sub esi, ebx
mov ecx, [.this.charWidth]
imul ebx, ecx
mov eax, esi
mul ecx
add ebx, [.this.left]
shl ebx, 16
lea ebx, [ebx + 0x10000 + eax + 1]
mov edx, [.this.selectColor]
mov ecx, [.this.top]
inc ecx
shl ecx, 16
mov cx, word[.this.height]
mcall SF_DRAW_RECT
.no_selection:
ret
endp
macro EditBox.__calcFontParameters{
local ..font6x9
mov ecx, [edi + EditBox.txColor.bgrf]
and ecx, 17FFFFFFh
shld edx, ecx, 8
and edx, 0xF
inc edx
shr ecx, 28
lea eax, [edx * 3]
lea ebx, [edx * 9 + 4]
jecxz ..font6x9
lea eax, [edx * 4]
lea ebx, [edx * 8 + 2]
shl ebx, 1
..font6x9:
shl eax, 1
mov [edi + EditBox.charWidth], eax
mov [edi + EditBox.height], ebx
}
macro EditBox.__terminateText{
local ..void_text
mov ecx, [edi + EditBox.text]
jecxz ..void_text
mov eax, [edi + EditBox.textLen]
mov word[ecx + eax], 0
..void_text:
}
macro EditBox.__calcCursorPos{
mov eax, [edi + EditBox.caretPos]
sub eax, [edi + EditBox.offset]
mov ecx, [edi + EditBox.charWidth]
mul ecx
add eax, [edi + EditBox.left]
inc eax
mov ecx, [edi + EditBox.top]
add ecx, 2
mov [edi + EditBox.cursorPos.x], ax
mov [edi + EditBox.cursorPos.y], cx
}
align 4
proc EditBox.__drawCursor
virtual at edi
.this EditBox
end virtual
mov edx, [.this.txColor.bgrf]
movzx ebx, [.this.cursorPos.x]
movzx ecx, [.this.cursorPos.y]
mov eax, ebx
shl ebx, 16
or ebx, eax
mov eax, ecx
shl ecx, 16
or ecx, eax
add cx, word[.this.height]
sub cx, 3
mcall SF_DRAW_LINE
ret
endp
align 4
proc EditBox.__draw
virtual at edi
.this EditBox
end virtual
EditBox.__terminateText
EditBox.__calcFontParameters
EditBox.__visibleSmbCount
mov [.this.visibleCount], ax
call EditBox.__calcOffset
or [.this.flags], ES.DRAWN_ONCE
call EditBox.__drawBorder
call EditBox.__drawBg
test [.this.flags], ES.FOCUS
jz .no_focus
EditBox.__calcCursorPos
call EditBox.__drawSelection
call EditBox.__drawText
jmp EditBox.__drawCursor
.no_focus:
jmp EditBox.__drawText
endp
align 4
proc edit_box_draw stdcall, .pthis
pusha
mov edi, [.pthis]
call EditBox.__draw
popa
ret
endp
align 4
proc EditBox.__processMouse
virtual at edi
.this EditBox
end virtual
test [.this.flags], ES.DRAWN_ONCE
jz .return
test [.this.flags], ES.DISABLED
jnz .return
mcall SF_MOUSE_GET, SSF_BUTTON
mov ecx, [.this.pp_mouseOwner]
test eax, 1 ; left button clicked
jnz .left_button_clicked
and [.this.flags], not ES.MOUSE_ON
jecxz @f
mov dword[ecx], 0
@@:
.return: ret
.left_button_clicked:
jecxz .mouse_is_free
cmp dword[ecx], 0
jz .mouse_is_free
cmp [ecx], edi
jne .mouse_blur
.mouse_is_free:
;получаем координаты мыши относительно 0 т.е всей области экрана
mcall SF_MOUSE_GET, SSF_WINDOW_POSITION
test [.this.flags], ES.MOUSE_ON
jz .mouse_not_captured
; mouse wig-wag
or [.this.flags], ES.SELECT
test eax, eax
js .mouse_left
shr eax, 16
sub eax, [.this.left]
jc .mouse_left
cmp [.this.width], eax
jc .mouse_right
xor edx, edx
div [.this.charWidth]
add eax, [.this.offset]
cmp eax, [.this.textLen]
jae @f
mov [.this.caretPos], eax
@@:
jmp .after_move
.mouse_right:
mov eax, [.this.caretPos]
cmp eax, [.this.textLen]
jae @f
inc [.this.caretPos]
@@:
jmp .after_move
.mouse_left:
cmp [.this.caretPos], 0
je @f
dec [.this.caretPos]
@@:
.after_move:
mov eax, [.this.caretPos]
cmp ax, [.this.selectionPos]
jne @f
and [.this.flags], not ES.SELECT
@@:
jmp EditBox.__draw
.mouse_not_captured:
mov ebx, [.this.top]
cmp ax, bx
jl .mouse_blur
add ebx, [.this.height]
cmp ax, bx
jg .mouse_blur
shr eax, 16
movzx ebx, word[.this.left]
cmp ax, bx
jl .mouse_blur
add bx, word[.this.width]
cmp ax, bx
jg .mouse_blur
; перемещение позиции курсора
xor edx, edx
sub eax, [.this.left]
div [.this.charWidth]
add eax, [.this.offset]
cmp eax, [.this.textLen]
cmova eax, [.this.textLen]
; обработка и перемещение выделения
test [.this.flags], ES.MOUSE_ON
jnz @f
mov [.this.caretPos], eax
mov [.this.selectionPos], ax
or [.this.flags], ES.MOUSE_ON
cmp [.this.pp_mouseOwner], 0
je @f
mov [ecx], edi
@@:
or [.this.flags], ES.FOCUS
and [.this.flags], not ES.SELECT
jmp EditBox.__draw
.mouse_blur:
test [.this.flags], ES.ALWAYS_FOCUS
jnz @f
and [.this.flags], not ES.FOCUS
@@:
and [.this.flags], not ES.SELECT
jmp EditBox.__draw
endp
align 4
proc edit_box_mouse stdcall, .pthis
pusha
mov edi, [.pthis]
call EditBox.__processMouse
popa
ret
endp
align 4
proc EditBox.__delSelection
virtual at edi
.this EditBox
end virtual
xor ecx, ecx
movzx edx, [.this.selectionPos]
mov eax, [.this.caretPos]
cmp edx, eax
je .return
jb @f
xchg edx, eax
@@:
mov ecx, [.this.textLen]
mov esi, [.this.text]
sub ecx, eax
lea ebp, [edx + ecx]
jecxz .end_selection
mov ebx, edi
mov edi, esi
add esi, eax
add edi, edx
rep movsb
mov edi, ebx
.end_selection:
mov [.this.caretPos], edx
mov [.this.textLen], ebp
and [.this.flags], not ES.SELECT
mov ecx, esp
.return: ret
endp
align 4
proc EditBox.__replaceSelectChar
virtual at edi
.this EditBox
end virtual
mov ecx, eax
movzx edx, [.this.selectionPos]
mov eax, [.this.caretPos]
cmp edx, eax
je .return
jb @f
xchg edx, eax
@@:
mov esi, [.this.text]
mov byte[esi + edx], cl
inc edx
mov ecx, [.this.textLen]
mov ebx, edi
mov edi, esi
add esi, eax
sub ecx, eax
lea eax, [edx + ecx]
jecxz .selected_at_end
add edi, edx
cmp esi, edi
je .one_smb_selection
rep movsb
.one_smb_selection:
.selected_at_end:
mov edi, ebx
mov [.this.textLen], eax
mov [.this.caretPos], edx
and [.this.flags], not ES.SELECT
.return: ret
endp
; edx != 0 - inserted, edx == 0 - not inserted
align 4
proc EditBox.__insertChar
virtual at edi
.this EditBox
end virtual
xor edx, edx
mov ecx, [.this.textLen]
cmp ecx, [.this.textMaxLen]
je .return
cmp ecx, [.this.caretPos]
jne .caret_not_at_end
mov edx, [.this.text]
mov [edx + ecx], al
inc [.this.textLen]
inc [.this.caretPos]
.return: ret
.caret_not_at_end:
mov esi, [.this.text]
test [.this.flags], ES.INSERT_MODE
jnz .insert_mode
sub ecx, [.this.caretPos]
mov edx, [.this.textLen]
lea esi, [esi + edx - 1]
mov ebx, edi
lea edi, [esi + 1]
std
rep movsb
cld
mov byte[edi], al
mov edi, ebx
inc [.this.textLen]
inc [.this.caretPos]
ret
.insert_mode:
mov edx, [.this.caretPos]
mov byte[esi + edx], al
inc edx
mov [.this.caretPos], edx
ret
endp
align 4
proc EditBox.__selectionToClipboard
virtual at edi
.this EditBox
end virtual
test [.this.flags], ES.SELECT
jz .return
movzx edx, [.this.selectionPos]
mov ecx, [.this.caretPos]
cmp edx, ecx
je .return
jb @f
xchg edx, ecx
@@:
sub ecx, edx
add ecx, sizeof.BClipBuf
mcall SF_SYS_MISC, SSF_MEM_ALLOC
cmp eax, 1
je .return
cmp eax, -1
je .return
mov [eax + BClipBuf.cbSize], ecx
mov [eax + BClipBuf.contentType], 0
mov [eax + BClipBuf.encoding], 1
mov esi, [.this.text]
add esi, edx
mov ebx, edi
lea edi, [eax + BClipBuf.data_]
sub ecx, sizeof.BClipBuf
rep movsb
mov edi, ebx
mov edx, eax
mov ecx, [eax + BClipBuf.cbSize]
mcall SF_CLIPBOARD, SSF_WRITE_CB
mcall SF_SYS_MISC, SSF_MEM_FREE, edx
.return: ret
endp
; .p_str - const parameter, never changes
align 4
proc EditBox.__insertStr c, .p_str
virtual at edi
.this EditBox
end virtual
mov ebx, [.p_str]
virtual at ebx
.str EditBox.InplaceStr
end virtual
locals
.startSel rd 1
.endSel rd 1
.restLen rd 1
.newLen rd 1
endl
mov eax, [.this.caretPos]
movzx edx, [.this.selectionPos]
test [.this.flags], ES.SELECT
cmovz edx, eax
cmp edx, eax
jb @f
xchg edx, eax
@@:
mov [.startSel], edx
mov [.endSel], eax
mov eax, [.str.len]
add eax, edx
mov ecx, [.this.textLen]
sub ecx, [.endSel]
mov [.restLen], ecx
add eax, ecx
xor edx, edx
mov ecx, [.this.textMaxLen]
inc ecx
div ecx
mov [.newLen], edx
mov ecx, edx
sub ecx, [.startSel]
jz .return
; check if we have enough place for remaining part of text after selection
cmp ecx, [.str.len]
jle .no_place_for_rest
;
local .newCaret:DWORD
mov esi, [.this.text]
sub ecx, [.str.len]
mov [.restLen], ecx
mov eax, [.startSel]
add eax, [.str.len]
mov [.newCaret], eax
push edi
mov edi, esi
add edi, eax
add esi, [.endSel]
cmp esi, edi
je @f
jb .std
;cld
rep movsb
sub edi, [.restLen]
jmp .rest_move_end
.std:
lea edi, [edi + ecx - 1]
lea esi, [esi + ecx - 1]
std
rep movsb
cld
inc edi
.rest_move_end:
@@:
mov ecx, [.str.len]
sub edi, ecx
mov esi, [.str.p_str]
rep movsb
pop edi
mov eax, [.newCaret]
mov [.this.caretPos], eax
mov eax, [.newLen]
mov [.this.textLen], eax
and [.this.flags], not ES.SELECT
.return: ret
.no_place_for_rest:
push edi
mov edi, [.this.text]
add edi, [.startSel]
mov esi, [.str.p_str]
rep movsb
pop edi
mov eax, [.newLen]
mov [.this.caretPos], eax
mov [.this.textLen], eax
and [.this.flags], not ES.SELECT
ret
endp
; const parameter, didn't change
align 4
proc BEdit.__validateStrNumeric c, .p_str
virtual at edi
.this EditBox
end virtual
mov ebx, [.p_str]
virtual at ebx
.str EditBox.InplaceStr
end virtual
xor eax, eax
mov ecx, [.str.len]
jecxz .return
mov esi, [.str.p_str]
@@:
lodsb
sub al, 0x30
cmp al, 9
ja .return
loop @b
xor eax, eax
.return:
ret
endp
align 4
proc EditBox.__processKey
.L_WIN = 0x02
.R_WIN = 0x04
.SHIFT = 0x03
.CTRL = 0x0C
.ALT = 0x30
virtual at edi
.this EditBox
end virtual
test [.this.flags], ES.DRAWN_ONCE
jz .return
test [.this.flags], ES.FOCUS
jz .return
test [.this.flags], ES.MOUSE_ON or ES.DISABLED
jnz .return
mov esi, eax ; save key state
;--------------------------------------
; this code for Win-keys, works with
; kernel SVN r.3356 or later
mcall SF_KEYBOARD, SSF_GET_CONTROL_KEYS
test ah, .L_WIN or .R_WIN
jnz .return
and [.this.flags], not (ES.ALT_ON or ES.CTRL_ON or ES.SHIFT_ON)
test eax, .SHIFT
jz @f
or [.this.flags], ES.SHIFT_ON
@@:
test eax, .CTRL
jz @f
or [.this.flags], ES.CTRL_ON
@@:
test eax, .ALT
jz @f
or [.this.flags], ES.ALT_ON
@@:
mov eax, esi; restore key state
ror eax, 8
test [.this.flags], ES.CTRL_ON
jz .skip_ctrl
cmp ah, SCAN_CODE_V
je .ctrl_plus_v
cmp ah, SCAN_CODE_A
je .ctrl_plus_a
cmp ah, SCAN_CODE_X
je .ctrl_plus_x
cmp ah, SCAN_CODE_C
je .ctrl_plus_c
jmp .ctrl_end_case
.ctrl_plus_v:
mcall SF_CLIPBOARD, SSF_GET_SLOT_COUNT
test eax, eax
js .ctrl_end_case
jz .ctrl_end_case
lea ecx, [eax - 1]
mcall SF_CLIPBOARD, SSF_READ_CB
cmp eax, -1
je .ctrl_end_case
cmp eax, 1
je .ctrl_end_case
mov ecx,[eax + BClipBuf.contentType]
; check for text
test ecx,ecx
jz @f
mov ecx, eax
mcall SF_SYS_MISC, SSF_MEM_FREE
ret
@@:
mov ecx,[eax+8]
; check for cp866
cmp cl,1
je @f
mov ecx, eax
mcall SF_SYS_MISC, SSF_MEM_FREE
ret
@@:
lea ecx, [eax + BClipBuf.data_]
mov [eax + EditBox.InplaceStr.p_str], ecx
sub [eax + EditBox.InplaceStr.len], sizeof.BClipBuf
push eax
test [.this.flags], ES.NUMERIC
jz @f
call BEdit.__validateStrNumeric
test eax, eax
jz @f
pop ecx
mcall SF_SYS_MISC, SSF_MEM_FREE
ret
@@:
call EditBox.__insertStr
pop ecx
mcall SF_SYS_MISC, SSF_MEM_FREE
jmp EditBox.__draw
.ctrl_plus_a:
mov ecx, [.this.textLen]
jecxz .ctrl_end_case
test [.this.flags], ES.SELECT
jz @f
cmp [.this.selectionPos], 0
jne @f
cmp [.this.caretPos], ecx
je .ctrl_end_case
@@:
mov [.this.selectionPos], 0
mov [.this.caretPos], ecx
or [.this.flags], ES.SELECT
jmp EditBox.__draw
.ctrl_plus_x:
call EditBox.__selectionToClipboard
call EditBox.__delSelection
jecxz .ctrl_end_case
jmp EditBox.__draw
.ctrl_plus_c:
call EditBox.__selectionToClipboard
.ctrl_end_case:
ret
.skip_ctrl:
cmp ah, SCAN_CODE_SPACE
ja .scan_after_space
cmp al, ASCII_KEY_BACK
jne .no_backspace
cmp [.this.textLen], 0
je .return
test [.this.flags], ES.SELECT
jnz @f
mov ecx, [.this.caretPos]
jecxz .return
dec ecx
mov [.this.selectionPos], cx
or [.this.flags], ES.SELECT
@@:
call EditBox.__delSelection
jmp EditBox.__draw
.no_backspace:
; skip unsupported scancodes
irp val, SCAN_CODE_TAB, SCAN_CODE_RETURN, SCAN_CODE_ESCAPE{
cmp ah, val
je .return
}
jmp EditBox.__processPrintableCharacter
.scan_after_space:
cmp ah, SCAN_CODE_DELETE
ja .return
cmp ah, SCAN_CODE_HOME
jb .return
cmp ax, SCAN_CODE_CLEAR shl 8 + ASCII_KEY_CLEAR
je .return
cmp al, ASCII_KEY_LEFT
jb EditBox.__processPrintableCharacter
and eax, 0x0F
jmp dword[.unlock_numpad_filtration + eax * 4]
.return: ret
.unlock_numpad_filtration:
dd .key_left,\ ; LEFT
.return,\ ; DOWN
.return,\ ; UP
.key_right,\ ; RIGHT
.key_home,\ ; HOME
.key_end,\ ; END
.key_delete,\ ; DELETE
.return,\ ; PGDN
.return,\ ; PGUP
.key_insert ; INSERT_MODE
.key_left:
mov ecx, [.this.caretPos]
jecxz @f
dec ecx
@@:
jmp .commit_caret_move
.key_right:
mov eax, [.this.textLen]
mov ecx, [.this.caretPos]
cmp ecx, eax
je @f
inc ecx
@@:
jmp .commit_caret_move
.key_home:
mov ecx, [.this.caretPos]
jecxz @f
xor ecx, ecx
@@:
jmp .commit_caret_move
.key_end:
mov eax, [.this.textLen]
cmp [.this.caretPos], eax
je @f
mov ecx, eax
@@:
; jmp .commit_caret_move
.commit_caret_move:
btr [.this.flags], bsf ES.SELECT
jnc .caret_no_select
test [.this.flags], ES.SHIFT_ON
jz @f
or [.this.flags], ES.SELECT
cmp ecx, [.this.caretPos]
je .return
mov [.this.caretPos], ecx
jmp EditBox.__draw
@@:
mov [.this.caretPos], ecx
mov eax, [.this.caretPos]
mov [.this.selectionPos], ax
jmp EditBox.__draw
.caret_no_select:
cmp ecx, [.this.caretPos]
je .return
mov eax, [.this.caretPos]
mov [.this.selectionPos], ax
mov [.this.caretPos], ecx
test [.this.flags], ES.SHIFT_ON
jz @f
cmp cx, [.this.selectionPos]
je @f
or [.this.flags], ES.SELECT
@@:
jmp EditBox.__draw
.key_delete:
cmp [.this.textLen], 0
je .return
test [.this.flags], ES.SELECT
jnz @f
mov ecx, [.this.caretPos]
cmp ecx, [.this.textLen]
je .return
inc ecx
mov [.this.selectionPos], cx
or [.this.flags], ES.SELECT
@@:
call EditBox.__delSelection
jmp EditBox.__draw
.key_insert:
xor [.this.flags], ES.INSERT_MODE
ret
endp
align 4
proc EditBox.__processPrintableCharacter
virtual at edi
.this EditBox
end virtual
test [.this.flags], ES.NUMERIC
jz @f
cmp al, '0'
jb .return
cmp al, '9'
ja .return
@@:
rol eax, 8
movzx eax, ah
test [.this.flags], ES.SELECT
jz @f
call EditBox.__replaceSelectChar
jmp EditBox.__draw
@@:
call EditBox.__insertChar
test edx, edx
jz .return
jmp EditBox.__draw
.return: ret
endp
align 4
proc edit_box_key_safe stdcall, .pthis, .key
pusha
mov edi, [.pthis]
mov eax, [.key]
call EditBox.__processKey
popa
ret
endp
align 4
proc edit_box_key stdcall, .pthis
pusha
mov edi, [.pthis]
call EditBox.__processKey
popa
ret
endp
align 4
proc edit_box_set_text stdcall, .pthis, .text
pusha
mov edx, [.pthis]
virtual at edx
.this EditBox
end virtual
mov ecx, [.this.textMaxLen]
mov esi, [.text]
mov edi, [.this.text]
@@:
lodsb
stosb
test al, al
loopnz @b
sub ecx, [.this.textMaxLen]
not ecx ; neg ecx; sub ecx, 1
mov [.this.caretPos], ecx
mov [.this.textLen], ecx
and [.this.flags], not ES.SELECT
mov edi, edx
call EditBox.__draw
popa
ret
endp