; _______________________________________________________________________________________
;| |
;| ..::FreshLib::.. Free, open source. Licensed under "BSD 2-clause" license." |
;|_______________________________________________________________________________________|
;
; Description: TEdit object class
;
; Target OS: Any
;
; Dependencies:
;
; Notes: Represents single line edit control.
;_________________________________________________________________________________________
module "TEdit library"
object TEdit, TWindow
._Text dd ?
._Len dd ? ; the length of the string in characters. (UTF-8)
._Start dd ? ; Where the string begins to be displayed in the edit window.
._Pos dd ? ; Position of the caret in the string.
._Sel dd ? ; Position of the selction in the string.
._drag_mode dd ?
._MarginLeft dd ?
._MarginRight dd ?
._Lock dd ?
param .Text, .GetText, .SetText
param .Selection, .GetSel, .ReplaceSel
param .MarginLeft, ._MarginLeft, ._MarginLeft
param .MarginRight, ._MarginRight, ._MarginRight
param .CaretPos, ._Pos, .SetCaretPos
method .Create, .parent
method .Destroy
method .GetText
method .SetText, .value
method .SetCaretPos, .value
method .HitTest, .x, .select
; system event methods
method .UpdateCaretPos
method .GetSel
method .ReplaceSel, .hString
method .Paint
method .EventKeyPress, .utf8, .scancode, .kbdState
method .EventKeyRelease, .scancode, .kbdState
method .EventButtonPress, .button, .kbdState, .x, .y
method .EventMouseMove, .x, .y, .kbdState
method .EventButtonRelease, .button, .kbdState, .x, .y
method .EventFocusIn
method .EventFocusOut
endobj
method TEdit.Create
begin
push ecx eax
inherited [.parent]
stdcall StrNew
mov ecx, [.self]
mov [ecx+TEdit._Text], eax
mov [ecx+TEdit._cursor], mcText
mov [ecx+TEdit.__want_focus], TRUE
pop eax ecx
return
endp
method TEdit.Destroy
begin
mov eax, [.self]
stdcall StrDel, [eax+TEdit._Text]
return
endp
method TEdit.GetText
begin
mov eax, [.self]
stdcall StrDup, [eax+TEdit._Text]
return
endp
method TEdit.GetSel
begin
pushad
mov esi, [.self]
xor edx, edx
mov ebx, [esi+TEdit._Pos]
mov ecx, [esi+TEdit._Sel]
cmp ebx, [esi+TEdit._Sel]
je .finish
jl @f
xchg ebx, ecx ; ebx=beg_selection; ecx=end_selection
@@:
stdcall StrOffsUtf8, [esi+TEdit._Text], ecx
mov ecx, eax
stdcall StrOffsUtf8, [esi+TEdit._Text], ebx
mov ebx, eax
stdcall StrPtr, [esi+TEdit._Text]
sub ebx, eax ; begin of the selection
sub ecx, eax
sub ecx, ebx
stdcall StrNew
mov edx, eax
stdcall StrCopyPart, edx, [esi+TEdit._Text], ebx, ecx
.finish:
mov [esp+4*regEAX], edx
popad
return
endp
; returns CF=1 if there was no selection.
; CF=0 if some text was deleted.
method TEdit.ReplaceSel ;, .hString
.beg dd ?
.end dd ?
.caret dd ?
begin
pushad
mov esi, [.self]
xor edx, edx
stdcall CaretShow, 0
mov [.caret], eax
mov ebx, [esi+TEdit._Pos]
mov ecx, [esi+TEdit._Sel]
cmp ebx, [esi+TEdit._Sel]
jne @f
lea edx, [edx+1] ; ret CF=1
@@:
jle @f
xchg ebx, ecx ; ebx=beg_selection; ecx=end_selection
@@:
add [esi+TEdit._Len], ebx
sub [esi+TEdit._Len], ecx
stdcall StrOffsUtf8, [esi+TEdit._Text], ecx
mov [.end], eax
stdcall StrOffsUtf8, [esi+TEdit._Text], ebx
mov [.beg], eax
stdcall StrPtr, [esi+TEdit._Text]
sub [.beg], eax ; begin of the selection
sub [.end], eax
stdcall StrSplit, [esi+TEdit._Text], [.end] ; end of the string
mov ecx, eax
stdcall StrSplit, [esi+TEdit._Text], [.beg]
stdcall StrDel, eax
cmp [.hString], 0
je .string_ok
stdcall StrCat, [esi+TEdit._Text], [.hString]
stdcall StrLenUtf8, [.hString], -1
add ebx, eax
add [esi+TEdit._Len], eax
.string_ok:
mov [esi+TEdit._Sel], ebx
mov [esi+TEdit._Pos], ebx
stdcall StrCat, [esi+TEdit._Text], ecx
stdcall StrDel, ecx
cmp [esi+TEdit._Lock], 0
jne .refresh_ok
exec esi, TEdit:Refresh
.refresh_ok:
stdcall CaretShow, [.caret]
shr edx, 1 ; CF = return value
popad
return
endp
method TEdit.SetText
begin
push esi eax
mov esi, [.self]
mov [esi+TEdit._Start], 0
mov [esi+TEdit._Pos], 0
mov [esi+TEdit._Sel], 0
lea eax, [esi+TEdit._Text]
stdcall SetString, eax, [.value]
stdcall StrLenUtf8, [esi+TEdit._Text], -1
mov [esi+TEdit._Len], eax
exec esi, TEdit:Refresh
pop eax esi
return
endp
method TEdit.Paint
.bounds TBounds
.selbeg dd ?
.selend dd ?
.clBack dd ?
.border dd ?
.caret dd ?
begin
pushad
mov esi, [.self]
cmp [esi+TWindow._canvas], 0
je .finish
stdcall CaretShow, FALSE
mov [.caret], eax
mov eax, [GUI.clEditBk]
mov ecx, [GUI.editBorder]
cmp esi, [__FocusedWindow]
jne @f
mov eax, [GUI.clEditBkFocused]
mov ecx, [GUI.editBorderFocused]
@@:
mov [.clBack], eax
mov [.border], ecx
; selection sort positions.
mov eax, [esi+TEdit._Pos]
mov ecx, [esi+TEdit._Sel]
cmp eax,ecx
jle @f
xchg eax, ecx
@@:
mov [.selbeg], eax
mov [.selend], ecx
; drawing bounds
mov ecx, [esi+TEdit._width]
mov eax, [esi+TEdit._height]
mov [.bounds.x], 0
mov [.bounds.y], 0
mov [.bounds.width], ecx
mov [.bounds.height], eax
lea eax, [.bounds]
stdcall [DrawBox], [esi+TWindow._canvas], eax, [.clBack], [.border], [GUI.boxBorderWidth]
mov eax, [GUI.boxBorderWidth]
add [.bounds.x], eax
add [.bounds.y], eax
shl eax, 1
sub [.bounds.width], eax
sub [.bounds.height], eax
mov eax, [esi+TEdit._MarginLeft]
mov ecx, [esi+TEdit._MarginRight]
add [.bounds.x], eax
sub [.bounds.width], eax
sub [.bounds.width], ecx
cmp [.bounds.width], 0
jle .enddraw
stdcall StrOffsUtf8, [esi+TEdit._Text], [esi+TEdit._Start]
mov edi, eax
stdcall DrawTextBox, [esi+TEdit._canvas], edi, [.bounds.x], [.bounds.y], [.bounds.width], [.bounds.height], 0, dtfAlignLeft or dtfAlignMiddle or dtfSingleLine, [GUI.DefaultFont], [GUI.clEditTxt]
; determine the selection
mov eax, [.selbeg]
xor ecx, ecx
sub eax, [esi+TEdit._Start]
jle .xsel_ok
stdcall StrOffsUtf8, edi, eax
sub eax, edi
stdcall GetTextBounds, edi, eax, [GUI.DefaultFont]
mov ecx, eax
.xsel_ok:
mov eax, [.selend]
sub eax, [esi+TEdit._Start]
jle .enddraw
stdcall StrOffsUtf8, edi, eax
sub eax, edi
stdcall GetTextBounds, edi, eax, [GUI.DefaultFont]
mov edx, [.bounds.width]
add edx, [.bounds.x]
cmp eax, edx
cmovg eax, edx
sub eax, ecx
add ecx, [.bounds.x]
stdcall BlendSolidRect, [esi+TWindow._canvas], ecx, [.bounds.y], eax, [.bounds.height], [GUI.clEditSel]
.enddraw:
inherited
stdcall CaretShow, [.caret]
.finish:
popad
return
endp
method TEdit.EventKeyPress ;, .utf8, .scancode, .kbdState
begin
pushad
mov esi, [.self]
mov [esi+TEdit._Lock], 1
get ebx, esi, TEdit:CaretPos
mov eax, [.utf8]
test eax, eax
jz .no_char
cmp eax, $20
jb .ctrl_char
cmp eax, $7f
je .no_char
stdcall StrNew
stdcall StrCharCat, eax, [.utf8]
push eax
exec esi, TEdit:ReplaceSel, eax
stdcall StrDel ; from the stack
mov ebx, [esi+TEdit._Pos]
jmp .set_pos
.ctrl_char:
OutputValue "Ascii:", eax, 10, 4
cmp eax, 1 ; Ctrl+A
je .select_all
cmp eax, 3 ; Ctrl+C
je .copy
cmp eax, 22 ; Ctrl+V
je .paste
jmp .no_char
.select_all:
mov ebx, [esi+TEdit._Len]
mov [esi+TEdit._Sel], 0
jmp .set_pos
.copy:
DebugMsg "Copy"
exec esi, TEdit:GetSel
test eax, eax
jz .finish
stdcall ClipboardWrite, eax
stdcall StrDel, eax
jmp .finish
.paste:
DebugMsg "Paste"
stdcall ClipboardRead
test eax, eax
jz .finish
push eax
exec esi, TEdit:ReplaceSel, eax
mov ebx, [esi+TEdit._Pos]
stdcall StrDel
jmp .endmove
.no_char:
mov eax, [.scancode]
cmp eax, keyLeftNumpad
je .left
cmp eax, keyLeft
je .left
cmp eax, keyRightNumpad
je .right
cmp eax, keyRight
je .right
cmp eax, keyHomeNumpad
je .home
cmp eax, keyHome
je .home
cmp eax, keyEndNumpad
je .end
cmp eax, keyEnd
je .end
cmp eax, keyDelNumpad
je .del
cmp eax, keyDelete
je .del
cmp eax, keyBackSpace
je .backdel
jmp .finish
.left:
cmp ebx, 0
jle .endmove
dec ebx
jmp .endmove
.right:
cmp ebx, [esi+TEdit._Len]
jae .endmove
inc ebx
jmp .endmove
.home:
xor ebx, ebx
jmp .endmove
.end:
mov ebx, [esi+TEdit._Len]
jmp .endmove
.backdel:
cmp ebx, [esi+TEdit._Sel]
jne .pos_ok
test ebx, ebx
jz .pos_ok ; can't delete more than the begning
dec ebx
mov [esi+TEdit._Pos], ebx
jmp .pos_ok
.del:
cmp ebx, [esi+TEdit._Sel]
jne .pos_ok
cmp ebx, [esi+TEdit._Len]
jae .endmove ; can't delete past the end.
inc ebx
mov [esi+TEdit._Pos], ebx
.pos_ok:
exec esi, TEdit:ReplaceSel, 0
mov ebx, [esi+TEdit._Pos]
.endmove:
test [.kbdState], maskShift
jnz .set_pos
mov [esi+TEdit._Sel], ebx
.set_pos:
mov [esi+TEdit._Lock], 0
set esi, TEdit:CaretPos, ebx
exec esi, TEdit:Refresh
.finish:
popad
return
endp
method TEdit.EventKeyRelease ;, .scancode, .kbdState
begin
return
endp
method TEdit.SetCaretPos
begin
pushad
mov ecx, [.self]
mov eax, [.value]
mov [ecx+TEdit._Pos], eax
exec ecx, TEdit:Paint
exec ecx, TEdit:UpdateCaretPos
popad
return
endp
method TEdit.UpdateCaretPos
.refresh dd ?
begin
pushad
mov esi, [.self]
xor eax, eax
mov [.refresh], eax
mov eax, [esi+TEdit._Pos]
cmp eax, [esi+TEdit._Start]
jge .compute_x
; scroll the text
and eax, $fffffff8
mov [esi+TEdit._Start], eax
inc [.refresh]
.compute_x:
stdcall StrPtr, [esi+TEdit._Text]
mov edi, eax
stdcall StrOffsUtf8, edi, [esi+TEdit._Pos]
mov ecx, eax
stdcall StrOffsUtf8, edi, [esi+TEdit._Start]
sub ecx, eax ; offset to char under cursor.
js .finish
.leftok:
mov edi, eax ; StrOffsUtf8 returns pointer to start of the text.
mov eax, ecx
jecxz @f
stdcall GetTextBounds, edi, ecx, [GUI.DefaultFont]
@@:
lea ecx, [eax+1] ; x coordinate of the cursor.
add ecx, [esi+TEdit._MarginLeft]
add ecx, [GUI.boxBorderWidth]
mov eax, [esi+TEdit._width]
sub eax, [esi+TEdit._MarginRight]
sub eax, [GUI.boxBorderWidth]
cmp eax, ecx
jg .caretok
mov eax, [esi+TEdit._Start]
add eax, 8
cmp eax, [esi+TEdit._Len]
jle @f
mov eax, [esi+TEdit._Len]
@@:
mov [esi+TEdit._Start], eax
inc [.refresh]
jmp .compute_x
.caretok:
cmp [.refresh], 0
je .canvas_ok
exec esi, TEdit:Paint
.canvas_ok:
mov edx, [esi+TEdit._height]
sub edx, 4
; add ecx, [GUI.boxBorderWidth]
stdcall CaretChange, ecx, 2, 1, edx
.finish:
popad
return
endp
method TEdit.EventButtonPress
begin
inherited [.button], [.kbdState], [.x], [.y]
push ecx
mov ecx, [.self]
mov [ecx+TEdit._drag_mode], 1
stdcall SetMouseCapture, ecx
exec [.self], TEdit:HitTest, [.x], FALSE
pop ecx
return
endp
method TEdit.EventMouseMove
begin
inherited [.x], [.y], [.kbdState]
push ecx
mov ecx, [.self]
cmp [ecx+TEdit._drag_mode], 0
je .finish
exec [.self], TEdit:HitTest, [.x], TRUE
.finish:
pop ecx
return
endp
method TEdit.EventButtonRelease
begin
inherited [.button], [.kbdState], [.x], [.y]
push ecx
mov ecx, [.self]
mov [ecx+TEdit._drag_mode], 0
stdcall SetMouseCapture, 0
pop ecx
return
endp
method TEdit.HitTest
.target dd ?
.start dd ?
.offset dd ?
begin
pushad
mov ebx, [.self]
mov ecx, [.x]
sub ecx, [GUI.boxBorderWidth]
sub ecx, [ebx+TEdit._MarginLeft]
mov [.target], ecx
stdcall StrPtr, [ebx+TEdit._Text]
mov [.start], eax
stdcall StrOffsUtf8, [.start], [ebx+TEdit._Start]
sub eax, [.start]
stdcall GetTextBounds, [.start], eax, [GUI.DefaultFont]
mov [.offset], eax ; the offset in pixels of the leftmost edge of the window.
; Binary search of the position.
mov edi, [ebx+TEdit._Len] ; current
mov esi, [ebx+TEdit._Len] ; upper limit
xor ecx, ecx ; lower limit
.search_loop:
stdcall StrOffsUtf8, [ebx+TEdit._Text], edi
sub eax, [.start]
stdcall GetTextBounds, [.start], eax, [GUI.DefaultFont]
sub eax, [.offset]
cmp eax, [.target]
jg .go_down
; go up
cmp edi, esi
je .found
mov ecx, edi
add edi, esi
sar edi, 1
cmp edi, ecx
je .found
jmp .search_loop
.go_down:
cmp edi, ecx
je .found
mov esi, edi
add edi, ecx
sar edi, 1
cmp edi, esi
je .found
jmp .search_loop
.found:
mov eax, [ebx+TEdit._Start]
add eax, edi
cmp [.select], 0
jne @f
mov [ebx+TEdit._Sel], edi
@@:
set ebx, TEdit:CaretPos, edi
exec ebx, TEdit:Refresh
.finish:
popad
return
endp
method TEdit.EventFocusIn
begin
stdcall CaretAttach, [.self]
stdcall CaretShow, TRUE
exec [.self], TEdit:UpdateCaretPos
inherited
return
endp
method TEdit.EventFocusOut
begin
stdcall CaretShow, FALSE
stdcall CaretAttach, 0
inherited
return
endp
endmodule