tui_panel.inc
8l_HeavyThing/tui_panel.inc
	; ------------------------------------------------------------------------
	; HeavyThing x86_64 assembly language library and showcase programs
	; Copyright Â© 2015, 2016 2 Ton Digital 
	; Homepage: https://2ton.com.au/
	; Author: Jeff Marrison <jeff@2ton.com.au>
	;       
	; This file is part of the HeavyThing library.
	;       
	; HeavyThing is free software: you can redistribute it and/or modify
	; it under the terms of the GNU General Public License, or
	; (at your option) any later version.
	;       
	; HeavyThing is distributed in the hope that it will be useful, 
	; but WITHOUT ANY WARRANTY; without even the implied warranty of
	; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
	; GNU General Public License for more details.
	;       
	; You should have received a copy of the GNU General Public License along
	; with the HeavyThing library. If not, see <http://www.gnu.org/licenses/>.
	; ------------------------------------------------------------------------
	;       
	; tui_panel.inc: panel goodies... a panel is:
	; basically the same as a background, only with a box drawn around it
	; and a title atop, and automatic padding on the inside so that children
	; a) don't have to worry about the box, and b) can't write over top of it
	;
	; this is also a classic case of the reason why appendchild/etc are virtual methods
	; .. we hook them and toss them into a bounds-constrained child of our own
	; such that appendchild works the same as elsewhere, despite them being handled
	; differently here
	;

if used tui_panel$vtable | defined include_everything

dalign
tui_panel$vtable:
        dq      tui_panel$cleanup, tui_panel$clone, tui_panel$draw, tui_object$redraw, tui_object$updatedisplaylist, tui_object$sizechanged
        dq      tui_object$timer, tui_object$layoutchanged, tui_object$move, tui_object$setfocus, tui_object$gotfocus, tui_object$lostfocus
        dq      tui_object$keyevent, tui_object$domodal, tui_object$endmodal, tui_object$exit, tui_object$calcbounds, tui_object$calcchildbounds
        dq      tui_panel$appendchild, tui_object$appendbastard, tui_panel$prependchild, tui_panel$contains, tui_panel$getchildindex
        dq      tui_panel$removechild, tui_object$removebastard, tui_object$removeallchildren, tui_object$removeallbastards
        dq      tui_object$getobjectsunderpoint, tui_object$flatten, tui_object$firekeyevent, tui_object$ontab, tui_object$onshifttab
        dq      tui_object$setcursor, tui_object$showcursor, tui_object$hidecursor, tui_object$click, tui_object$clicked

end if

tui_panel_title_ofs = tui_background_size
tui_panel_titlecolors_ofs = tui_background_size + 8
tui_panel_titletext_ofs = tui_background_size + 16
tui_panel_guts_ofs = tui_background_size + 24
tui_panel_user_ofs = tui_background_size + 32		; not used in here

tui_panel_size = tui_background_size + 40



	; most panel instances i create aren't really descended from, but we are separating the $new and $init
	; such that it will be trivial to create either panels directly or descend from them either way

	; it is assumed that the pointer passed to the $init* functions already has (or will have) a valid vtable pointer

if used tui_panel$init_copy | defined include_everything
	; two arguments: rdi == tui_panel we are initialising, rsi == source tui_panel
	; this calls tui_background$init_copy and then sets up our own vars
falign
tui_panel$init_copy:
	prolog	tui_panel$init_copy
	sub	rsp, 16
	mov	[rsp], rdi
	mov	[rsp+8], rsi
	call	tui_background$init_copy
	; so that made a copy of all our children, and all our tui_object settings, and our tui_background settings
	; set our title colors
	mov	rsi, [rsp+8]
	mov	rdi, [rsi+tui_panel_title_ofs]
	call	string$copy
	mov	rdi, [rsp]
	mov	[rdi+tui_panel_title_ofs], rax
	; copy the titlecolors
	mov	rsi, [rsp+8]
	mov	rdi, [rsp]
	mov	rdx, [rsi+tui_panel_titlecolors_ofs]
	mov	[rdi+tui_panel_titlecolors_ofs], rdx
	; next we need to make a copy of our titletext
	mov	rsi, [rsp+8]
	mov	rdi, [rsi+tui_panel_titletext_ofs]
	mov	rcx, [rdi]
	call	qword [rcx+tui_vclone]
	mov	rdi, [rsp]
	mov	[rdi+tui_panel_titletext_ofs], rax
	; last but not least, since all our children already got cloned, we need to set our guts pointer correctly
	mov	rdi, [rdi+tui_children_ofs]
	; this -cannot- be null, provided that a real panel was created in the first place
	mov	rdx, [_list_first]
	; move to its next
	mov	rdx, [rdx+_list_nextofs]
	; the child at rdx is our second child, so pull its object
	mov	rdi, [rdx+_list_valueofs]
	mov	rdi, [rdi+tui_children_ofs]
	mov	rdx, [_list_first]
	; move to its next
	mov	rdx, [rdx+_list_nextofs]
	mov	rdi, [rsp]
	; the child at rdx is our guts object
	mov	rsi, [rdx+_list_valueofs]
	mov	[rdi+tui_panel_guts_ofs], rsi
	add	rsp, 16
	epilog
end if

if used tui_panel$nvsetup | defined include_everything
	; three arguments: rdi == tui_panel, rsi == source titlestring (we'll copy it), edx = titlecolors
falign
tui_panel$nvsetup:
	prolog	tui_panel$nvsetup
	sub	rsp, 16
	mov	[rsp], rdi
	mov	[rdi+tui_panel_titlecolors_ofs], rdx
	mov	rdi, rsi
	call	string$copy
	mov	rdi, [rsp]
	mov	[rdi+tui_panel_title_ofs], rax
	mov	rcx, [rdi+tui_panel_titlecolors_ofs]
	; title text is next
	mov	rdx, rax		; filltext
	mov	rdi, [rax]		; string length
	add	rdi, 2			; + 2
	mov	esi, 1			; height
	mov	r8d, tui_textalign_center
	call	tui_label$new_ii
	mov	rdi, [rsp]
	mov	[rdi+tui_panel_titletext_ofs], rax


	; our "guts" container is next up...
	; what we want is an hbox atop of padding 1 height, same for bottom, left and right 1 x by 100%
	; and a guts fill in between the left/right ones that consumes 100%,100% of remaining space
	; then, with our overridden appendchild, children go into our guts container and not our
	; direct child list (this way, we preserve our border)

	; first up, we need to add to our _actual_ child list a horizontal spacer
	movq	xmm0, [_math_onehundred]
	call	tui_hspacer$new_d
	mov	rsi, rax
	mov	rdi, [rsp]		; our tui_panel
	call	tui_object$appendchild	; NOT our overridden one!


	mov	edi, tui_object_size
	call	heap$alloc
	mov	[rsp+8], rax
	mov	qword [rax], tui_object$simple_vtable
	mov	rdi, rax
	movq	xmm0, [_math_onehundred]
	movq	xmm1, [_math_onehundred]
	call	tui_object$init_dd
	mov	rdi, [rsp+8]
	; set its layout to horizontal:
	mov	dword [rdi+tui_layout_ofs], tui_layout_horizontal
	; now we need a vertical spacer
	movq	xmm0, [_math_onehundred]
	call	tui_vspacer$new_d
	mov	rdi, [rsp+8]
	mov	rsi, rax
	call	tui_object$appendchild	; we don't use the vtable here because we know it is a tui_object

	; now we need another 100.0 x 100.0 for our guts itself
	mov	edi, tui_object_size
	call	heap$alloc
	mov	rcx, [rsp]		; our tui_panel object
	mov	[rcx+tui_panel_guts_ofs], rax	; save our guts object
	mov	rdi, rax
	mov	qword [rax], tui_object$simple_vtable
	movq	xmm0, [_math_onehundred]
	movq	xmm1, [_math_onehundred]
	call	tui_object$init_dd
	mov	rcx, [rsp]
	mov	rsi, [rcx+tui_panel_guts_ofs]
	mov	rdi, [rsp+8]
	call	tui_object$appendchild	; add it to our hbox
	
	; now we need one more vertical spacer added to it
	movq	xmm0, [_math_onehundred]
	call	tui_vspacer$new_d
	mov	rdi, [rsp+8]
	mov	rsi, rax
	call	tui_object$appendchild	; we don't use the vtable here because we know it is a tui_object

	; so now, we need to add the hbox itself to our real children list
	mov	rdi, [rsp]
	mov	rsi, [rsp+8]
	call	tui_object$appendchild	; NOT our overridden one!

	; and last but not least, we need one more horizontal spacer
	movq	xmm0, [_math_onehundred]
	call	tui_hspacer$new_d
	mov	rsi, rax
	mov	rdi, [rsp]
	call	tui_object$appendchild	; NOT our overridden one!

	add	rsp, 16
	epilog
end if


if used tui_panel$init_rect | defined include_everything
	; five arguments: rdi == tui_panel, rsi == pointer to a bounds rect, rdx == title string (we'll copy it), ecx = boxcolors, r8d = titlecolors
falign
tui_panel$init_rect:
	prolog	tui_panel$init_rect
	sub	rsp, 24
	mov	[rsp], rdi
	mov	[rsp+8], rdx
	mov	[rsp+16], r8
	mov	edx, ' '		; fillchar for tui_background
					; ecx already set with our background colors
	call	tui_background$init_rect
	mov	rdi, [rsp]
	mov	rsi, [rsp+8]
	mov	rdx, [rsp+16]
	call	tui_panel$nvsetup
	add	rsp, 24
	epilog
end if


if used tui_panel$init_id | defined include_everything
	; six arguments: rdi == tui_panel, esi = width, xmm0 == heightperc, rdx == title string (we'll copy it), ecx = boxcolors, r8d = titlecolors
falign
tui_panel$init_id:
	prolog	tui_panel$init_id
	sub	rsp, 24
	mov	[rsp], rdi
	mov	[rsp+8], rdx
	mov	[rsp+16], r8
	mov	edx, ' '		; fillchar for tui_background
					; ecx already set with our background colors
	call	tui_background$init_id
	mov	rdi, [rsp]
	mov	rsi, [rsp+8]
	mov	rdx, [rsp+16]
	call	tui_panel$nvsetup
	add	rsp, 24
	epilog
end if

if used tui_panel$init_di | defined include_everything
	; six arguments: rdi == tui_panel, xmm0 = widthperc, esi = height, rdx == title string (we'll copy it), ecx = boxcolors, r8d = titlecolors
falign
tui_panel$init_di:
	prolog	tui_panel$init_di
	sub	rsp, 24
	mov	[rsp], rdi
	mov	[rsp+8], rdx
	mov	[rsp+16], r8
	mov	edx, ' '		; fillchar for tui_background
					; ecx already set with our background colors
	call	tui_background$init_di
	mov	rdi, [rsp]
	mov	rsi, [rsp+8]
	mov	rdx, [rsp+16]
	call	tui_panel$nvsetup
	add	rsp, 24
	epilog
end if

if used tui_panel$init_dd | defined include_everything
	; six arguments: rdi == tui_panel, xmm0 = widthperc, xmm1 = heightperc, rsi == title string (we'll copy it), edx = boxcolors, ecx = titlecolors
falign
tui_panel$init_dd:
	prolog	tui_panel$init_dd
	sub	rsp, 24
	mov	[rsp], rdi
	mov	[rsp+8], rsi
	mov	[rsp+16], rcx
	mov	esi, ' '		; fillchar for tui_background
					; edx already set with our background colors
	call	tui_background$init_dd
	mov	rdi, [rsp]
	mov	rsi, [rsp+8]
	mov	rdx, [rsp+16]
	call	tui_panel$nvsetup
	add	rsp, 24
	epilog
end if

if used tui_panel$init_ii | defined include_everything
	; six arguments: rdi == tui_panel, esi = width, edx = height, rcx = title string (we'll copy it), r8d = boxcolors, r9d = titlecolors
falign
tui_panel$init_ii:
	prolog	tui_panel$init_ii
	sub	rsp, 24
	mov	[rsp], rdi
	mov	[rsp+8], rcx
	mov	[rsp+16], r9
	mov	ecx, ' '		; fillchar for tui_background
					; r8d already set with our background colors
	call	tui_background$init_ii
	mov	rdi, [rsp]
	mov	rsi, [rsp+8]
	mov	rdx, [rsp+16]
	call	tui_panel$nvsetup
	add	rsp, 24
	epilog
end if

if used tui_panel$new_rect | defined include_everything
	; four arguments: rdi == pointer to a bounds rect, rsi = title string (we'll copy it), edx = boxcolors, ecx = titlecolors
	; returns new tui_panel in rax
falign
tui_panel$new_rect:
	prolog	tui_panel$new_rect
	push	rdi rsi rdx rcx
	mov	edi, tui_panel_size
	call	heap$alloc
	mov	qword [rax], tui_panel$vtable
	pop	r8 rcx rdx rsi
	mov	rdi, rax
	push	rax
	call	tui_panel$init_rect
	pop	rax
	epilog
end if


if used tui_panel$new_id | defined include_everything
	; five arguments: edi = width, xmm0 = heightperc, rsi = title string (we'll copy it), edx = boxcolors, ecx = titlecolors
	; returns new tui_panel in rax
falign
tui_panel$new_id:
	prolog	tui_panel$new_id
	sub	rsp, 48
	mov	[rsp], rdi
	movq	[rsp+8], xmm0
	mov	[rsp+16], rsi
	mov	[rsp+24], rdx
	mov	[rsp+32], rcx
	mov	edi, tui_panel_size
	call	heap$alloc
	mov	qword [rax], tui_panel$vtable
	mov	[rsp+40], rax
	mov	rdi, rax
	mov	rsi, [rsp]
	movq	xmm0, [rsp+8]
	mov	rdx, [rsp+16]
	mov	rcx, [rsp+24]
	mov	r8, [rsp+32]
	call	tui_panel$init_id
	mov	rax, [rsp+40]
	add	rsp, 48
	epilog
end if

if used tui_panel$new_di | defined include_everything
	; five arguments: xmm0 = widthperc, edi = height, rsi = title string (we'll copy it), edx = boxcolors, ecx = titlecolors
	; returns new tui_panel in rax
falign
tui_panel$new_di:
	prolog	tui_panel$new_di
	sub	rsp, 48
	movq	[rsp], xmm0
	mov	[rsp+8], rdi
	mov	[rsp+16], rsi
	mov	[rsp+24], rdx
	mov	[rsp+32], rcx
	mov	edi, tui_panel_size
	call	heap$alloc
	mov	qword [rax], tui_panel$vtable
	mov	[rsp+40], rax
	mov	rdi, rax
	movq	xmm0, [rsp]
	mov	rsi, [rsp+8]
	mov	rdx, [rsp+16]
	mov	rcx, [rsp+24]
	mov	r8, [rsp+32]
	call	tui_panel$init_di
	mov	rax, [rsp+40]
	add	rsp, 48
	epilog
end if

if used tui_panel$new_dd | defined include_everything
	; five arguments: xmm0 = widthperc, xmm1 = heightperc, rdi = title string (we'll copy it), esi = boxcolors, edx = titlecolors
	; returns new tui_panel in rax
falign
tui_panel$new_dd:
	prolog	tui_panel$new_dd
	sub	rsp, 48
	movq	[rsp], xmm0
	movq	[rsp+8], xmm1
	mov	[rsp+16], rdi
	mov	[rsp+24], rsi
	mov	[rsp+32], rdx
	mov	edi, tui_panel_size
	call	heap$alloc
	mov	qword [rax], tui_panel$vtable
	mov	[rsp+40], rax
	mov	rdi, rax
	movq	xmm0, [rsp]
	movq	xmm1, [rsp+8]
	mov	rsi, [rsp+16]
	mov	rdx, [rsp+24]
	mov	rcx, [rsp+32]
	call	tui_panel$init_dd
	mov	rax, [rsp+40]
	add	rsp, 48
	epilog
end if

if used tui_panel$new_ii | defined include_everything
	; five arguments: edi = width, esi = height, rdx = title string (we'll copy it), ecx = boxcolors, r8d = titlecolors
	; returns new tui_panel in rax
falign
tui_panel$new_ii:
	prolog	tui_panel$new_ii
	sub	rsp, 48
	mov	[rsp], rdi
	mov	[rsp+8], rsi
	mov	[rsp+16], rdx
	mov	[rsp+24], rcx
	mov	[rsp+32], r8
	mov	edi, tui_panel_size
	call	heap$alloc
	mov	qword [rax], tui_panel$vtable
	mov	[rsp+40], rax
	mov	rdi, rax
	mov	rsi, [rsp]
	mov	rdx, [rsp+8]
	mov	rcx, [rsp+16]
	mov	r8, [rsp+24]
	mov	r9, [rsp+32]
	call	tui_panel$init_ii
	mov	rax, [rsp+40]
	add	rsp, 48
	epilog
end if


if used tui_panel$cleanup | defined include_everything
	; single argument: rdi == tui_panel to cleanup
	; NOTE: we do not delete the object itself, only cleanup what we allocated
falign
tui_panel$cleanup:
	prolog	tui_panel$cleanup
	; our titletext is not an actual child in our list, so we have to free that, and our titlestring itself
	; the rest will get cleaned up for us by tui_object$cleanup (noting we are actually descended from tui_background, but it has no special cleanup)
	push	rdi
	mov	rdi, [rdi+tui_panel_title_ofs]
	call	heap$free		; the title string
	mov	rsi, [rsp]
	mov	rdi, [rsi+tui_panel_titletext_ofs]
	mov	rcx, [rdi]
	call	qword [rcx+tui_vcleanup]	; call the title text's cleanup vmethod
	mov	rsi, [rsp]
	mov	rdi, [rsi+tui_panel_titletext_ofs]
	call	heap$free
	; to cleanup the rest of our goods, we need to call tui_object's cleanup
	mov	rdi, [rsp]
	call	tui_object$cleanup
	pop	rdi
	epilog
end if


if used tui_panel$clone | defined include_everything
	; single argument: rdi == source tui_panel to clone/make a copy of
	; returns new tui_panel copy in rax
falign
tui_panel$clone:
	prolog	tui_panel$clone
	push	rdi
	mov	edi, tui_panel_size
	call	heap$alloc
	mov	qword [rax], tui_panel$vtable
	mov	rsi, [rsp]
	mov	[rsp], rax
	mov	rdi, rax
	call	tui_panel$init_copy
	pop	rax
	epilog
end if

if used tui_panel$draw | defined include_everything
	; single argument: rdi == tui_panel
falign
tui_panel$draw:
	prolog	tui_panel$draw
	push	rdi
	call	tui_background$nvfill
	mov	rdi, [rsp]
	cmp	dword [rdi+tui_width_ofs], 0
	je	.diddlysquat
	cmp	dword [rdi+tui_height_ofs], 0
	je	.diddlysquat
	; first up, draw our box
	xor	esi, esi			; x
	xor	edx, edx			; y
	mov	ecx, [rdi+tui_width_ofs]
	mov	r8d, [rdi+tui_height_ofs]
	mov	r9d, [rdi+tui_bgcolors_ofs]
	call	tui_object$nvbox
	; so long as our title's length is nonzero, draw our title as well
	mov	rdi, [rsp]
	mov	rsi, [rdi+tui_panel_title_ofs]
	cmp	qword [rsi], 0
	je	.notitle
	mov	rdi, [rdi+tui_panel_titletext_ofs]
	mov	rdx, [rdi]
	call	qword [rdx+tui_vdraw]		; call our titletext's draw method first
	mov	rdi, [rsp]
	mov	rsi, [rdi+tui_panel_titletext_ofs]
	; non-empty title, draw it with left/right TEEs
	mov	ecx, [rdi+tui_width_ofs]
	mov	r8d, [rsi+tui_width_ofs]
	sub	ecx, r8d
	cmp	ecx, 0
	jl	.notitle
	shl	ecx, 1				; space leftover / 2 << 2 (for byte offset)
	and	ecx, not 3
	mov	r8, [rdi+tui_text_ofs]
	mov	r9, [rdi+tui_attr_ofs]
	add	r8, rcx
	add	r9, rcx
	sub	r8, 4
	sub	r9, 4
	cmp	r8, [rdi+tui_text_ofs]
	jb	.noltee
	mov	dword [r8], 0x2524		; LTEE
calign
.noltee:
	add	r8, 4
	add	r9, 4
	mov	r10, [rsi+tui_text_ofs]
	mov	r11, [rsi+tui_attr_ofs]
	mov	edx, [rsi+tui_width_ofs]
	shl	edx, 2				; in bytes
	mov	rax, r8				; where we are at in the buffer currently
	sub	rax, [rdi+tui_text_ofs]		; less the start == how many bytes we have used so far
	mov	rcx, [rdi+tui_width_ofs]
	shl	rcx, 2
	sub	rcx, rax
	cmp	rdx, rcx
	cmova	rdx, rcx			; make sure we don't overrun the buffer
	push	r8 r9 r10 r11 rdx
	mov	rdi, r8
	mov	rsi, r10
	call	memcpy
	pop	rdx r11 r10 r9 r8
	push	r8 r9 r10 r11 rdx
	mov	rdi, r9
	mov	rsi, r11
	call	memcpy
	pop	rdx r11 r10 r9 r8
	add	r8, rdx
	add	r9, rdx
	mov	rdi, [rsp]
	mov	ecx, [rdi+tui_width_ofs]
	shl	ecx, 2
	mov	r10, [rdi+tui_text_ofs]
	add	r10, rcx
	cmp	r8, r10
	jae	.nortee
	mov	dword [r8], 0x251c
calign
.nortee:
	mov	rdi, [rsp]
calign
.notitle:
	mov	rdx, [rdi]		; load up our vtable
	call	qword [rdx+tui_vupdatedisplaylist]
	pop	rdi
	epilog
calign
.diddlysquat:
	pop	rdi
	epilog
end if

if used tui_panel$appendchild | defined include_everything
	; two arguments: rdi == our tui_panel, rsi == new object to add
falign
tui_panel$appendchild:
	prolog	tui_panel$appendchild
	mov	rdi, [rdi+tui_panel_guts_ofs]
	mov	rcx, [rdi]			; load up its vtable
	call	qword [rcx+tui_vappendchild]
	epilog
end if


if used tui_panel$prependchild | defined include_everything
	; two arguments: rdi == our tui_panel, rsi == new object to add
falign
tui_panel$prependchild:
	prolog	tui_panel$prependchild
	mov	rdi, [rdi+tui_panel_guts_ofs]
	mov	rcx, [rdi]			; load up its vtable
	call	qword [rcx+tui_vprependchild]
	epilog
end if


if used tui_panel$contains | defined include_everything
	; two arguments: rdi == our tui_panel, rsi == object to test for
	; returns zero in eax if rsi is not in our children list, or 1 in eax if it does
falign
tui_panel$contains:
	prolog	tui_panel$contains
	mov	rdi, [rdi+tui_panel_guts_ofs]
	mov	rcx, [rdi]			; load up its vtable
	call	qword [rcx+tui_vcontains]
	epilog
end if


if used tui_panel$getchildindex | defined include_everything
	; two arguments: rdi == our tui_panel, rsi == object to test for
	; returns unsigned index in eax, or -1 if it isn't found
falign
tui_panel$getchildindex:
	prolog	tui_panel$getchildindex
	mov	rdi, [rdi+tui_panel_guts_ofs]
	mov	rcx, [rdi]			; load up its vtable
	call	qword [rcx+tui_vgetchildindex]
	epilog
end if


if used tui_panel$removechild | defined include_everything
	; two arguments: rdi == our tui_panel, rsi == object to remove
	; NOTE: does _NOT_ delete/heap$free the child
	; if we do indeed remove it, we fire a layoutchanged vmethod call to redraw
	; returns zero in rax if we didn't find it, or returns the child pointer if we did
falign
tui_panel$removechild:
	prolog	tui_panel$removechild
	mov	rdi, [rdi+tui_panel_guts_ofs]
	mov	rcx, [rdi]			; load up its vtable
	call	qword [rcx+tui_vremovechild]
	epilog
end if
