include 'widgets.inc'

;------------------------------------------------------------------------------

proc box_t_init _obj, _left, _top, _width, _height
	push	eax
	mov	eax, [_obj]
	push	[_left] [_top]
	push	[_width] [_height]
	pop	[eax + box_t.size.height] [eax + box_t.size.width]
	pop	[eax + box_t.pos.top] [eax + box_t.pos.left]
	pop	eax
	ret
endp

;------------------------------------------------------------------------------

proc ptr_array_t_init _obj
	push	eax
	mov	eax, [_obj]
	mov	[eax + ptr_array_t.pointers], 0
	mov	[eax + ptr_array_t.count], 0
	pop	eax
	ret
endp

proc ptr_array_t_add _obj, _ptr
	push	eax ebx
	mov	ebx, [_obj]
	mov	eax, [ebx + ptr_array_t.count]
	inc	eax
	mov	[ebx + ptr_array_t.count], eax
	shl	eax, 2
	push	eax
	stdcall mem.realloc, [ebx + ptr_array_t.pointers], eax
	mov	[ebx + ptr_array_t.pointers], eax
	pop	eax
	mov	ebx, [ebx + ptr_array_t.pointers]
	push	[_ptr]
	popd	[ebx + eax - 4]
	pop	ebx eax
	ret
endp

;------------------------------------------------------------------------------

proc widget_is_kind_of _obj, _class
	push	ebx
	mov	eax, [_obj]
	mov	ebx, [_class]
	mov	eax, [eax + widget_t.class]
    @@: or	eax, eax
	jz	@f
	cmp	eax, ebx
	mov	eax, [eax + class_t._]
	jne	@b
	xor	eax, eax
	inc	eax
    @@: pop	ebx
	ret
endp

proc widget_set_box _obj, _box
	push	eax ebx
	mov	ebx, [_obj]
	mov	eax, [_box]
	push	[eax + box_t.pos.left] [eax + box_t.pos.top]
	pop	[ebx + widget_t.box.pos.top] [ebx + widget_t.box.pos.left]
	push	[eax + box_t.size.width] [eax + box_t.size.height]
	pop	[ebx + widget_t.box.size.height] [ebx + widget_t.box.size.width]
	pop	ebx eax
	ret
endp

proc widget_paint _obj
	push	eax ebx
	mov	ebx, [_obj]
	mov	eax, [ebx + widget_t.class]
	mov	eax, [eax + class_t.vftbl]
	stdcall [eax + widget_vftbl_t.paint_widget], ebx
	pop	ebx eax
	ret
endp

;------------------------------------------------------------------------------

proc container_class_add_widget _obj, _widget
	push	eax ebx
	mov	ebx, [_obj]
	lea	eax, [ebx + container_t.children]
	stdcall ptr_array_t_add, eax, [_widget]
	pop	ebx eax
	ret
endp

;------------------------------------------------------------------------------

proc layout_class_calc_min_size _obj
	push	ebx
	mov	ebx, [_obj]
	mov	eax, [ebx + widget_t.class]
	mov	eax, [eax + class_t.vftbl]
	stdcall [eax + widget_vftbl_t.calc_min_size], ebx
	pop	ebx
	ret
endp

proc layout_class_paint_widget _obj
	pushad
	mov	ebx, [_obj]
	mov	esi, [ebx + container_t.children]
	mov	ecx, [ebx + container_t.children.count]
    @@: dec	ecx
	js	@f
	lodsd
	mov	edx, [eax + widget_t.class]
	mov	edx, [edx + class_t.vftbl]
	stdcall [edx + widget_vftbl_t.paint_widget], eax
	jmp	@b
    @@: popad
	ret
endp

proc layout_class_add_widget _obj, _widget, _sizing
	push	ebx
	mov	ebx, [_obj]
	stdcall container_class_add_widget, ebx, [_widget]
	lea	eax, [ebx + layout_t.sizings]
	stdcall ptr_array_t_add, eax, [_sizing]
	pop	ebx
	ret
endp

proc layout_class_arrange_widgets _obj
	push	ebx
	mov	ebx, [_obj]
	mov	eax, [ebx + widget_t.class]
	mov	eax, [eax + class_t.vftbl]
	stdcall [eax + layout_vftbl_t.arrange_widgets], ebx
	pop	ebx
	ret
endp

;------------------------------------------------------------------------------

proc hbox_layout_class_new, _spacing
	stdcall mem.alloc, sizeof.layout_t
	mov	[eax + widget_t.class], hbox_layout_class
	push	[_spacing]
	pop	[eax + layout_t.spacing]
	ret
endp

proc hbox_layout_class_calc_min_size _obj
	push	ebx ecx edx esi edi
	mov	ebx, [_obj]
	mov	esi, [ebx + container_t.children]
	mov	ecx, [ebx + container_t.children.count]
    @@: dec	ecx
	js	@f
	lodsd
	mov	edx, [eax + widget_t.class]
	mov	edx, [edx + class_t.vftbl]
	stdcall [edx + widget_vftbl_t.calc_min_size], eax
	jmp	@b
    @@:
	mov	esi, [ebx + container_t.children]
	mov	ecx, [ebx + container_t.children.count]
	xor	edx, edx
	xor	edi, edi
    @@: dec	ecx
	js	@f
	lodsd
	add	edx, [eax + widget_t.min_size.width]
	cmp	edi, [eax + widget_t.min_size.height]
	jae	@b
	mov	edi, [eax + widget_t.min_size.height]
	jmp	@b
    @@: mov	[ebx + widget_t.min_size.width], edx
	mov	[ebx + widget_t.min_size.height], edi
	mov	eax, [ebx + container_t.children.count]
	dec	eax
	imul	eax, [ebx + layout_t.spacing]
	add	[ebx + widget_t.min_size.width], eax
	pop	edi esi edx ecx ebx
	ret
endp

proc hbox_layout_class_arrange_widgets _obj
locals
  num_expanding dd ?
  delta 	dd ?
  left		dd ?
endl
	pushad
	mov	ebx, [_obj]

	stdcall hbox_layout_class_calc_min_size, ebx

	mov	eax, [ebx + widget_t.min_size.width]
	cmp	eax, [ebx + widget_t.box.size.width]
	jbe	@f
	mov	[ebx + widget_t.box.size.width], eax

    @@: mov	eax, [ebx + widget_t.min_size.height]
	cmp	eax, [ebx + widget_t.box.size.height]
	jbe	@f
	mov	[ebx + widget_t.box.size.height], eax

    @@: mov	esi, [ebx + layout_t.sizings]
	mov	ecx, [ebx + container_t.children.count]
	xor	edx, edx
	xor	edi, edi
    @@: dec	ecx
	js	@f
	lodsd
	or	edx, eax
	test	eax, LAYOUT_HEXPAND
	jz	@b
	inc	edi
	jmp	@b
    @@: mov	[ebx + layout_t.sizing], edx

	mov	[num_expanding], edi
	mov	eax, [ebx + widget_t.box.size.width]
	sub	eax, [ebx + widget_t.min_size.width]
	mov	[delta], eax

	mov	esi, [ebx + container_t.children]
	mov	edi, [ebx + layout_t.sizings]
	xor	ecx, ecx
	push	[ebx + widget_t.box.pos.left]
	pop	[left]

  .next_child:
	inc	ecx
	cmp	ecx, [ebx + container_t.children.count]
	ja	.exit
	lodsd

	mov	edx, [eax + widget_t.min_size.width]
	push	[left] [ebx + widget_t.box.pos.top]
	pop	[eax + widget_t.box.pos.top] [eax + widget_t.box.pos.left]
	push	edx [eax + widget_t.min_size.height]
	pop	[eax + widget_t.box.size.height] [eax + widget_t.box.size.width]
	add	edx, [ebx + layout_t.spacing]
	add	[left], edx

	test	dword[edi + ecx * 4 - 4], LAYOUT_HEXPAND
	jz	.check_vexpand

	push	eax ebx
	mov	eax, [delta]
	mov	ebx, [num_expanding]
	cdq
	idiv	ebx
	xchg	edx, eax
	pop	ebx eax

	add	[eax + widget_t.box.size.width], edx
	add	[left], edx
	sub	[delta], edx
	dec	[num_expanding]

  .check_vexpand:
	test	dword[edi + ecx * 4 - 4], LAYOUT_VEXPAND
	jz	.check_for_layout

	push	[ebx + widget_t.box.size.height]
	pop	[eax + widget_t.box.size.height]

  .check_for_layout:
	xchg	eax, edx
	stdcall widget_is_kind_of, edx, layout_class
	or	eax, eax
	jz	.next_child
	mov	eax, [edx + widget_t.class]
	mov	eax, [eax + class_t.vftbl]
	stdcall [eax + layout_vftbl_t.arrange_widgets], edx
	jmp	.next_child

  .exit:
	popad
	ret
endp

;------------------------------------------------------------------------------

proc vbox_layout_class_new, _spacing
	stdcall mem.alloc, sizeof.layout_t
	mov	[eax + widget_t.class], vbox_layout_class
	push	[_spacing]
	pop	[eax + layout_t.spacing]
	ret
endp

proc vbox_layout_class_calc_min_size _obj
	push	ebx ecx edx esi edi
	mov	ebx, [_obj]
	mov	esi, [ebx + container_t.children]
	mov	ecx, [ebx + container_t.children.count]
    @@: dec	ecx
	js	@f
	lodsd
	mov	edx, [eax + widget_t.class]
	mov	edx, [edx + class_t.vftbl]
	stdcall [edx + widget_vftbl_t.calc_min_size], eax
	jmp	@b
    @@:
	mov	esi, [ebx + container_t.children]
	mov	ecx, [ebx + container_t.children.count]
	xor	edx, edx
	xor	edi, edi
    @@: dec	ecx
	js	@f
	lodsd
	add	edx, [eax + widget_t.min_size.height]
	cmp	edi, [eax + widget_t.min_size.width]
	jae	@b
	mov	edi, [eax + widget_t.min_size.width]
	jmp	@b
    @@: mov	[ebx + widget_t.min_size.height], edx
	mov	[ebx + widget_t.min_size.width], edi
	mov	eax, [ebx + container_t.children.count]
	dec	eax
	imul	eax, [ebx + layout_t.spacing]
	add	[ebx + widget_t.min_size.height], eax
	pop	edi esi edx ecx ebx
	ret
endp

proc vbox_layout_class_arrange_widgets _obj
locals
  num_expanding dd ?
  delta 	dd ?
  top		dd ?
endl
	pushad
	mov	ebx, [_obj]

	stdcall vbox_layout_class_calc_min_size, ebx

	mov	eax, [ebx + widget_t.min_size.width]
	cmp	eax, [ebx + widget_t.box.size.width]
	jbe	@f
	mov	[ebx + widget_t.box.size.width], eax

    @@: mov	eax, [ebx + widget_t.min_size.height]
	cmp	eax, [ebx + widget_t.box.size.height]
	jbe	@f
	mov	[ebx + widget_t.box.size.height], eax

    @@: mov	esi, [ebx + layout_t.sizings]
	mov	ecx, [ebx + container_t.children.count]
	xor	edx, edx
	xor	edi, edi
    @@: dec	ecx
	js	@f
	lodsd
	or	edx, eax
	test	eax, LAYOUT_VEXPAND
	jz	@b
	inc	edi
	jmp	@b
    @@: mov	[ebx + layout_t.sizing], edx

	mov	[num_expanding], edi
	mov	eax, [ebx + widget_t.box.size.height]
	sub	eax, [ebx + widget_t.min_size.height]
	mov	[delta], eax

	mov	esi, [ebx + container_t.children]
	mov	edi, [ebx + layout_t.sizings]
	xor	ecx, ecx
	push	[ebx + widget_t.box.pos.top]
	pop	[top]

  .next_child:
	inc	ecx
	cmp	ecx, [ebx + container_t.children.count]
	ja	.exit
	lodsd

	mov	edx, [eax + widget_t.min_size.height]
	push	[ebx + widget_t.box.pos.left] [top]
	pop	[eax + widget_t.box.pos.top] [eax + widget_t.box.pos.left]
	push	[eax + widget_t.min_size.width] edx
	pop	[eax + widget_t.box.size.height] [eax + widget_t.box.size.width]
	add	edx, [ebx + layout_t.spacing]
	add	[top], edx

	test	dword[edi + ecx * 4 - 4], LAYOUT_VEXPAND
	jz	.check_hexpand

	push	eax ebx
	mov	eax, [delta]
	mov	ebx, [num_expanding]
	cdq
	idiv	ebx
	xchg	edx, eax
	pop	ebx eax

	add	[eax + widget_t.box.size.height], edx
	add	[top], edx
	sub	[delta], edx
	dec	[num_expanding]

  .check_hexpand:
	test	dword[edi + ecx * 4 - 4], LAYOUT_HEXPAND
	jz	.check_for_layout

	push	[ebx + widget_t.box.size.width]
	pop	[eax + widget_t.box.size.width]

  .check_for_layout:
	xchg	eax, edx
	stdcall widget_is_kind_of, edx, layout_class
	or	eax, eax
	jz	.next_child
	mov	eax, [edx + widget_t.class]
	mov	eax, [eax + class_t.vftbl]
	stdcall [eax + layout_vftbl_t.arrange_widgets], edx
	jmp	.next_child

  .exit:
	popad
	ret
endp

;------------------------------------------------------------------------------

proc rect_class_new _width, _height
	stdcall mem.alloc, sizeof.widget_t
	mov	[eax + widget_t.class], rect_class
	push	[_width] [_height]
	pop	[eax + widget_t.min_size.height] [eax + widget_t.min_size.width]
	ret
endp

proc rect_class_calc_min_size _obj
	; do nothing - min size already initialized by `new`
	ret
endp

proc rect_class_paint_widget _obj
	pushad
	mov	edi, [_obj]
	mov	bx, word[edi + widget_t.box.pos.left]
	push	bx
	shl	ebx, 16
	pop	bx
	add	bx, word[edi + widget_t.box.size.width]
	dec	bx
	mov	cx, word[edi + widget_t.box.pos.top]
	push	cx
	shl	ecx, 16
	pop	cx
	mcall	38, , , 0x00ff0000
	add	cx, word[edi + widget_t.box.size.height]
	dec	cx
	push	cx
	shl	ecx, 16
	pop	cx
	mcall
	mov	cx, word[edi + widget_t.box.pos.top]
	rol	ecx, 16
	mov	bx, word[edi + widget_t.box.pos.left]
	mcall
	add	bx, word[edi + widget_t.box.size.width]
	dec	bx
	push	bx
	shl	ebx, 16
	pop	bx
	mcall
	popad
	ret
endp

;------------------------------------------------------------------------------

proc spacer_class_new
	push	ebx
	stdcall mem.alloc, sizeof.widget_t
	mov	[eax + widget_t.class], spacer_class
	push	0 0
	pop	[eax + widget_t.min_size.height] [eax + widget_t.min_size.width]
	pop	ebx
	ret
endp

proc spacer_class_calc_min_size _obj
	ret
endp

proc spacer_class_paint_widget _obj
	ret
endp

;------------------------------------------------------------------------------
;------------------------------------------------------------------------------

widget_class_name   db 'widget_class', 0

widget_class_vftbl dd \
  0, \
  0

widget_class dd \
  0, \
  widget_class_name, \
  widget_class_vftbl

;------------------------------------------------------------------------------

container_class_name db 'container_class', 0

container_class_vftbl dd \
  0, \
  0, \
  container_class_add_widget

container_class dd \
  widget_class, \
  container_class_name, \
  container_class_vftbl

;------------------------------------------------------------------------------

layout_class_name db 'layout_class', 0

layout_class_vftbl dd \
  0, \
  layout_class_paint_widget, \
  layout_class_add_widget, \
  0

layout_class dd \
  container_class, \
  layout_class_name, \
  layout_class_vftbl

;------------------------------------------------------------------------------

hbox_layout_class_name db 'hbox_layout_class', 0

hbox_layout_class_vftbl dd \
  hbox_layout_class_calc_min_size, \
  layout_class_paint_widget, \
  layout_class_add_widget, \
  hbox_layout_class_arrange_widgets

hbox_layout_class dd \
  layout_class, \
  hbox_layout_class_name, \
  hbox_layout_class_vftbl

;------------------------------------------------------------------------------

vbox_layout_class_name db 'vbox_layout_class', 0

vbox_layout_class_vftbl dd \
  vbox_layout_class_calc_min_size, \
  layout_class_paint_widget, \
  layout_class_add_widget, \
  vbox_layout_class_arrange_widgets

vbox_layout_class dd \
  layout_class, \
  vbox_layout_class_name, \
  vbox_layout_class_vftbl

;------------------------------------------------------------------------------
;------------------------------------------------------------------------------

rect_class_name   db 'rect_class', 0

rect_class_vftbl dd \
  rect_class_calc_min_size, \
  rect_class_paint_widget

rect_class dd \
  widget_class, \
  rect_class_name, \
  rect_class_vftbl

;------------------------------------------------------------------------------

spacer_class_name   db 'spacer_class', 0

spacer_class_vftbl dd \
  spacer_class_calc_min_size, \
  spacer_class_paint_widget

spacer_class dd \
  widget_class, \
  spacer_class_name, \
  spacer_class_vftbl
