; ***************************************************************************
; copyright   : (C) 2026 by Martin Preuss
; email       : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome".                              *
; * Please see toplevel file COPYING of that project for license details.   *
; ***************************************************************************

#ifndef AQH_AVR_GUI2_LAYOUT_ASM
#define AQH_AVR_GUI2_LAYOUT_ASM


; ***************************************************************************
; defines


.equ LAYOUT_CTX_OFFS_TOTALSIZE_LO = 0
.equ LAYOUT_CTX_OFFS_TOTALSIZE_HI = 1
.equ LAYOUT_CTX_OFFS_BORDERS      = 2
.equ LAYOUT_CTX_OFFS_SPACING      = 3
.equ LAYOUT_CTX_OFFS_NUMITEMS     = 4
.equ LAYOUT_CTX_OFFS_ITEMS        = 5

.equ LAYOUT_CTX_ITEM_OFFS_POS_LO          = 0
.equ LAYOUT_CTX_ITEM_OFFS_POS_HI          = 1
.equ LAYOUT_CTX_ITEM_OFFS_SIZE_LO         = 2
.equ LAYOUT_CTX_ITEM_OFFS_SIZE_HI         = 3
.equ LAYOUT_CTX_ITEM_OFFS_FLAGS           = 4
.equ LAYOUT_CTX_ITEM_SIZE                 = 5   ; CAVEAT: change code in LayoutCtx_new if size changed!



; ***************************************************************************
; macros


.macro M_LAYOUT_FOREVERY_CONT
  push yl
    push yh
      ldi zl, LOW(@0)
      ldi zh, HIGH(@0)
      rcall layoutForEveryObjectContiguous
    pop yh
  pop yl
.endmacro



.macro M_LAYOUT_FOREVERY_SKIPPED
  push yl
    push yh
      ldi zl, LOW(@0)
      ldi zh, HIGH(@0)
      rcall layoutForEveryObjectSkipped
    pop yh
  pop yl
.endmacro



; ***************************************************************************
; code

.cseg



; ---------------------------------------------------------------------------
; @routine LayoutCtx_new @global
;
; @param r16 number of entries to reserve
; @return X pointer to new layout context
; @clobbers r16, r17, r18, r19, r24, r25

LayoutCtx_new:
  push r16
    clr r17
    mov r24, r16
    mov r25, r17
    lsl r24      ; x2
    rol r25
    lsl r24      ; x4
    rol r25
    add r24, r16
    adc r25, r17 ; x5
    ldi r16, LOW(LAYOUT_CTX_OFFS_ITEMS)
    ldi r17, HIGH(LAYOUT_CTX_OFFS_ITEMS)
    add r24, r16
    adc r25, r17
    ; alloc on heap
    push r24
      push r25
        bigcall Heap_AllocAndZero      ; (r16, r17, r18, r19, r24, r25)
      pop r25
    pop r24
  pop r16
  brcc LayoutCtx_new_ret
  adiw xh:xl, LAYOUT_CTX_OFFS_NUMITEMS
  st X, r16                            ; store number of items
  sbiw xh:xl, LAYOUT_CTX_OFFS_NUMITEMS
  sec
LayoutCtx_new_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_free @global
;
; @param X pointer to layout context
; @clobbers r16, r17, r24, r25, X

LayoutCtx_free:
  bigjmp Heap_Free   ; (r16, r17, r24, r25, X)
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_CreateContextFor1D @global
;
; @param Y pointer to widget
; @return CFLAG set if context created, cleared on error
; @return X pointer to newly created context
; @return R16 number of children
; @clobbers any, !Y

LayoutCtx_CreateContextFor1D:
  ; create layout context
  bigcall OBJ_CountDirectChildren       ; r16=num of children (r18, r19)
  tst r16
  clc
  breq LayoutCtx_CreateContextFor1D_ret
  rcall LayoutCtx_CreateContextForN
LayoutCtx_CreateContextFor1D_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_CreateContextForN @global
;
; @param Y pointer to widget
; @param R16  number of items
; @return CFLAG set if context created, cleared on error
; @return X pointer to newly created context
; @clobbers any, !R16, !Y

LayoutCtx_CreateContextForN:
  tst r16
  clc
  breq LayoutCtx_CreateContextForN_ret
  push r16
    bigcall LayoutCtx_new               ; X=new ctx (r16, r17, r18, r19, r24, r25)
  pop r16
  brcc LayoutCtx_CreateContextForN_ret
  ; set borders and spacing in layout context
  bigcall Widget_GetBorderAndSpacing    ; (none)
  adiw xh:xl, LAYOUT_CTX_OFFS_BORDERS
  st X+, r23                            ; store border
  st X+, r22                            ; store spacing
  st X, r16                             ; store number of items
  sbiw xh:xl, LAYOUT_CTX_OFFS_NUMITEMS  ; back to beginning
  sec
LayoutCtx_CreateContextForN_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_LayoutExpand
;
; @param X pointer to layout context
; @clobbers r16-r25

LayoutCtx_LayoutExpand:
  rcall layoutCtxCalcExpandPerExpandableItem  ; r17:r16=space to add to expandable items (r16-r25)
  rcall layoutCtxSetSizeWithExtra             ; (r20, r21, r22, r24)
  rcall LayoutCtx_SetPos                      ; (r16-r25)
  ret
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_LayoutSpread
;
; @param X pointer to layout context
; @clobbers r16-r25

LayoutCtx_LayoutSpread:
;  rcall layoutCtxCalcExpandPerExpandableItem  ; r17:r16=space to add to expandable items (r16-r25)
  rcall layoutCtxCalcSpreadValue              ; r17:r16=space to add to expandable items and between (r16-r25)
  push r16
    push r17
      rcall layoutCtxSetSizeWithExtra         ; expand expandable items (r20, r21, r22, r24)
    pop r17
  pop r16
  rcall layoutCtxSetPosWithExtra              ; (r16-r25)
  ret
; @end




; ---------------------------------------------------------------------------
; @routine LayoutCtx_GetMaxSize
;
; @param X pointer to ctx
; @return r19:r18 maximum default size
; @clobbers r18, r19, r20, r21, r24

LayoutCtx_GetMaxSize:
  push xl
    push xh
      adiw xh:xl, LAYOUT_CTX_OFFS_NUMITEMS
      ld r24, X+                                             ; X now points to first item
      clr r18
      clr r19
LayoutCtx_GetMaxSize_loop:
        adiw xh:xl, LAYOUT_CTX_ITEM_OFFS_SIZE_LO
        ld r20, X+
        ld r21, X
        sbiw xh:xl, (LAYOUT_CTX_ITEM_OFFS_SIZE_LO+1)
        cp r18, r20
        cpc r19, r21
        brcc LayoutCtx_GetMaxSize_next
        mov r18, r20
        mov r19, r21
LayoutCtx_GetMaxSize_next:
        adiw xh:xl, LAYOUT_CTX_ITEM_SIZE
        dec r24
      brne LayoutCtx_GetMaxSize_loop
    pop xh
  pop xl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_SetFixedSize
;
; @param X pointer to ctx
; @param r19:r18 new size
; @clobbers r24

LayoutCtx_SetFixedSize:
  push xl
    push xh
      adiw xh:xl, LAYOUT_CTX_OFFS_NUMITEMS
      ld r24, X+                                             ; X now points to first item
LayoutCtx_SetFixedSize_loop:
        adiw xh:xl, LAYOUT_CTX_ITEM_OFFS_SIZE_LO
        st X+, r18
        st X, r19
        adiw xh:xl, (LAYOUT_CTX_ITEM_SIZE-LAYOUT_CTX_ITEM_OFFS_SIZE_HI)
        dec r24
      brne LayoutCtx_SetFixedSize_loop
    pop xh
  pop xl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_SetFixedPos
;
; @param X pointer to ctx
; @param r19:r18 new pos
; @clobbers r24

LayoutCtx_SetFixedPos:
  push xl
    push xh
      adiw xh:xl, LAYOUT_CTX_OFFS_NUMITEMS
      ld r24, X+                                             ; X now points to first item
LayoutCtx_SetFixedPos_loop:
        adiw xh:xl, LAYOUT_CTX_ITEM_OFFS_POS_LO
        st X+, r18
        st X, r19
        adiw xh:xl, (LAYOUT_CTX_ITEM_SIZE-LAYOUT_CTX_ITEM_OFFS_POS_HI)
        dec r24
      brne LayoutCtx_SetFixedPos_loop
    pop xh
  pop xl
  ret
; @end






; ---------------------------------------------------------------------------
; @routine layoutCtxSetSizeWithExtra
;
; Copy default size to size field and add extra space to expandable items.
;
; @param X pointer to layout context
; @param r17:r16 number of bytes to add to expandable items
; @clobbers r20, r21, r22, r24

layoutCtxSetSizeWithExtra:
  push xl
    push xh
      adiw xh:xl, LAYOUT_CTX_OFFS_NUMITEMS
      ld r24, X+                                 ; number of items (X now points to first item)
layoutCtxSetSizeWithExtra_loop:
        adiw xh:xl, LAYOUT_CTX_ITEM_OFFS_SIZE_LO
        ld r20, X+
        ld r21, X
        sbiw xh:xl, (LAYOUT_CTX_ITEM_OFFS_SIZE_LO+1)
        adiw xh:xl, LAYOUT_CTX_ITEM_OFFS_FLAGS
        ld r22, X
        sbiw xh:xl, LAYOUT_CTX_ITEM_OFFS_FLAGS
        andi r22, 3
        cpi r22, WIDGET_PACK_FILLED
        brne layoutCtxSetSizeWithExtra_store
        add r20, r16                             ; only add for expandable items
        adc r21, r17
layoutCtxSetSizeWithExtra_store:
        adiw xh:xl, LAYOUT_CTX_ITEM_OFFS_SIZE_LO
        st X+, r20
        st X, r21
        adiw xh:xl, (LAYOUT_CTX_ITEM_SIZE-LAYOUT_CTX_ITEM_OFFS_SIZE_HI)
        dec r24
      brne layoutCtxSetSizeWithExtra_loop
    pop xh
  pop xl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_SetPos
;
; Set pos of all items.
;
; @param X pointer to layout context
; @clobbers r16-r25

LayoutCtx_SetPos:
  push xl
    push xh
      adiw xh:xl, LAYOUT_CTX_OFFS_BORDERS
      ld r18, X+                                     ; borders (start calculated size with one border)
      clr r19
      ld r22, X+                                     ; spacing
      clr r23
      ld r24, X+                                     ; number of items (X now points to first item)
LayoutCtx_SetPos_loop:                                ; r19:r18=current pos, r22=spacing, r23=border, X=1st item
        adiw xh:xl, LAYOUT_CTX_ITEM_OFFS_POS_LO      ; store pos
        st X+, r18
        st X, r19
        sbiw xh:xl, (LAYOUT_CTX_ITEM_OFFS_POS_LO+1)
        adiw xh:xl, LAYOUT_CTX_ITEM_OFFS_SIZE_LO
        ld r20, X+
        ld r21, X
        sbiw xh:xl, (LAYOUT_CTX_ITEM_OFFS_SIZE_LO+1)
        add r18, r20                                 ; add current item size
        adc r19, r21
        add r18, r22                                 ; add spacing
        adc r19, r23
        adiw xh:xl, LAYOUT_CTX_ITEM_SIZE             ; next item
        dec r24
      brne LayoutCtx_SetPos_loop
    pop xh
  pop xl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxSetPosWithExtra
;
; Layout items in their dimension according to their packing flags and also add
; extra space to spacings and borders
;
; @param X pointer to layout context
; @clobbers r16-r25

layoutCtxSetPosWithExtra:
  push xl
    push xh
      adiw xh:xl, LAYOUT_CTX_OFFS_BORDERS
      ld r18, X+                                 ; borders (start calculated size with one border)
      clr r19
      ld r22, X+                                 ; spacing
      clr r23
      ld r24, X+                                 ; number of items (X now points to first item)
      add r22, r16                               ; add extra space to spacing
      adc r23, r17
      add r18, r16                               ; add space to border
      adc r19, r17
layoutCtxSetPosWithExtra_loop:                   ; r19:r18=current pos, r22=spacing, r23=border, X=1st item
        adiw xh:xl, LAYOUT_CTX_ITEM_OFFS_POS_LO
        st X+, r18
        st X, r19
        sbiw xh:xl, (LAYOUT_CTX_ITEM_OFFS_POS_LO+1)
        adiw xh:xl, LAYOUT_CTX_ITEM_OFFS_SIZE_LO
        ld r20, X+
        ld r21, X
        sbiw xh:xl, (LAYOUT_CTX_ITEM_OFFS_SIZE_LO+1)
        add r18, r20                                 ; add current item size
        adc r19, r21
        add r18, r22                                 ; add spacing
        adc r19, r23
        adiw xh:xl, LAYOUT_CTX_ITEM_SIZE             ; next item
        dec r24
      brne layoutCtxSetPosWithExtra_loop
    pop xh
  pop xl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxPack
;
; @param  X       pointer to current item
; @param  r21:r20 default size of the widget
; @return r19:r18 calculated pos
; @return r21:r20 calculated size
; @clobbers r16-r19, r25

layoutCtxPack:
  adiw xh:xl, LAYOUT_CTX_ITEM_OFFS_POS_LO
  ld r18, X+    ; pos lo
  ld r19, X+    ; pos hi
  ld r16, X+    ; size lo
  ld r17, X+    ; size hi
  ld r25, X     ; flags
  sbiw xh:xl, LAYOUT_CTX_ITEM_OFFS_FLAGS
  rjmp layoutPack
; @end



; ---------------------------------------------------------------------------
; @routine layoutPack
;
; @param  r17:r16 space available to the child widget
; @param  r21:r20 default size of the child widget
; @param  r19:r18 pos
; @param  r25     pack flags
; @return r19:r18 calculated pos
; @return r21:r20 calculated size
; @clobbers r16-r19, r25

layoutPack:
  sub r16, r20  ; r17:r16=(size-defaultSize)
  sbc r17, r21
  brcs layoutPack_done
  andi r25, 3
  cpi r25, WIDGET_PACK_END
  breq layoutPack_end
  cpi r25, WIDGET_PACK_CENTER
  breq layoutPack_center
  cpi r25, WIDGET_PACK_FILLED
  breq layoutPack_filled
  rjmp layoutPack_done
layoutPack_end:
  add r18, r16                 ; just add difference to pos
  adc r19, r17
  rjmp layoutPack_done
layoutPack_center:
  lsr r17
  ror r16
  add r18, r16                 ; just add half difference to pos
  adc r19, r17
  rjmp layoutPack_done
layoutPack_filled:
  add r16, r20                 ; get total size back
  adc r17, r21
  mov r20, r16
  mov r21, r17
layoutPack_done:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxCalcExpandPerExpandableItem
;
; @param X pointer to layout context
; @return r17:r16 space per expandable item
; @clobbers r16-r25

layoutCtxCalcExpandPerExpandableItem:
  rcall LayoutCtx_CountExpandableItems            ; r16=num (r17, r24)
  tst r16
  breq layoutCtxCalcExpandPerExpandableItem_none  ; no expandable item, nothing to expand
  push r16
    rcall layoutCtxCalcAvailableExtraSpace        ; r21:r20=available extra space (r16-r24)
  pop r16
  brcc layoutCtxCalcExpandPerExpandableItem_none  ; jmp if no extra space
  mov r22, r16
  clr r23
  bigcall Utils_Divu16_16_16                      ; r17:r16=space per expandable item
  rjmp layoutCtxCalcExpandPerExpandableItem_ret
layoutCtxCalcExpandPerExpandableItem_none:
  clr r16
  clr r17
layoutCtxCalcExpandPerExpandableItem_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxCalcSpreadValue
;
; @param X pointer to layout context
; @return r17:r16 value to add for borders, spacings and expandable items
; @clobbers r16-r25

layoutCtxCalcSpreadValue:
  rcall LayoutCtx_CountExpandableItems            ; r16=num (r17, r24)
  adiw xh:xl, LAYOUT_CTX_OFFS_NUMITEMS
  ld r24, X
  sbiw xh:xl, LAYOUT_CTX_OFFS_NUMITEMS
  add r16, r24
  inc r16
  tst r16
  breq layoutCtxCalcSpreadValue_none              ; no expandable item, nothing to expand
  
  push r16
    rcall layoutCtxCalcAvailableExtraSpace        ; r21:r20=available extra space (r16-r24)
  pop r16
  brcc layoutCtxCalcExpandPerExpandableItem_none  ; jmp if no extra space
  mov r22, r16
  clr r23
  bigcall Utils_Divu16_16_16                      ; r17:r16=space per expandable item (r25)
  rjmp layoutCtxCalcSpreadValue_ret
layoutCtxCalcSpreadValue_none:
  clr r16
  clr r17
layoutCtxCalcSpreadValue_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxCalcAvailableExtraSpace
;
; @param  X       pointer to layout context
; @return CFLAG   set if there is extra space, cleared otherwise
; @return r21:r20 available extra space
; @clobbers r16-r24

layoutCtxCalcAvailableExtraSpace:
  rcall LayoutCtx_CalcMinimumSize               ; r19:r18=size (r16, r17, r22, r23, r24)
  adiw xh:xl, LAYOUT_CTX_OFFS_TOTALSIZE_LO
  ld r20, X+
  ld r21, X
  sbiw xh:xl, (LAYOUT_CTX_OFFS_TOTALSIZE_LO+1)
  sub r20, r18
  sbc r21, r19
  ; flip carry flag
  rol r16      ; rotate CF into r16
  com r16      ; flip bit
  ror r16      ; rotate bit 0 into CF
  brcs layoutCtxCalcAvailableExtraSpace_ret
  clr r20
  clr r21
  clc
layoutCtxCalcAvailableExtraSpace_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_CalcMinimumSize
;
; Calculate minimum size needed for the given items (containing borders and
; spacings) by using the defaultSize fields of the items.
;
; @param X pointer to layout ctx object
; @return r19:r18 calculated size (including borders and spacings)
; @clobbers r16, r17, r22, r23, r24

LayoutCtx_CalcMinimumSize:
  push xl
    push xh
      adiw xh:xl, LAYOUT_CTX_OFFS_BORDERS
      ld r18, X+                                 ; borders (start calculated size with one border)
      clr r19
      ld r22, X+                                 ; spacing
      clr r23
      ld r24, X+                                 ; number of items (X now points to first item)
      lsl r18                                    ; add border at beginning and end
      rol r19
LayoutCtx_CalcMinimumSize_loop:
        adiw xh:xl, LAYOUT_CTX_ITEM_OFFS_SIZE_LO
        ld r16, X+                               ; LAYOUT_CTX_ITEM_OFFS_DEFAULTSIZE_LO
        ld r17, X                                ; LAYOUT_CTX_ITEM_OFFS_DEFAULTSIZE_HI
        adiw xh:xl, (LAYOUT_CTX_ITEM_SIZE-LAYOUT_CTX_ITEM_OFFS_SIZE_HI)
        add r18, r16   ; add size
        adc r19, r17
        add r18, r22   ; add spacing
        adc r19, r23
        dec r24
      brne LayoutCtx_CalcMinimumSize_loop
      sub r18, r22     ; sub last spacing
      sbc r19, r23
    pop xh
  pop xl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_CountExpandableItems
;
; Calculate minimum size needed for the given items (containing borders and
; spacings) by using the defaultSize fields of the items.
;
; @param X pointer to layout ctx object
; @return r16 number of expandable items
; @clobbers r17, r24

LayoutCtx_CountExpandableItems:
  push xl
    push xh
      adiw xh:xl, LAYOUT_CTX_OFFS_NUMITEMS
      ld r24, X+                                 ; number of items (X now points to first item)
      clr r16
LayoutCtx_CountExpandableItems_loop:
        adiw xh:xl, LAYOUT_CTX_ITEM_OFFS_FLAGS
        ld r17, X
        adiw xh:xl, (LAYOUT_CTX_ITEM_SIZE-LAYOUT_CTX_ITEM_OFFS_FLAGS)
        andi r17, 3
        cpi r17, WIDGET_PACK_FILLED
        brne LayoutCtx_CountExpandableItems_next
        inc r16
LayoutCtx_CountExpandableItems_next:
        dec r24
      brne LayoutCtx_CountExpandableItems_loop
    pop xh
  pop xl
  ret
; @end







; ---------------------------------------------------------------------------
; @routine LayoutCtx_ReadXDimsContiguous
;
; @param X pointer to ctx
; @param Y pointer to widget
; @clobbers R16, r18, r19, r24, Z

LayoutCtx_ReadXDimsContiguous:
  ldi zl, LOW(layoutCtxCbCopyWidgetXToItem)
  ldi zh, HIGH(layoutCtxCbCopyWidgetXToItem)
  rjmp layoutCtxForEveryChildContiguous      ; (R16, r18, r19, r24)
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_ReadXDimsSkipped
;
; @param X pointer to ctx
; @param Y pointer to first widget to handle
; @param r25 number of widgets to skip in each round

LayoutCtx_ReadXDimsSkipped:
  ldi zl, LOW(layoutCtxCbCopyWidgetXToItem)
  ldi zh, HIGH(layoutCtxCbCopyWidgetXToItem)
  rjmp layoutCtxForEveryChildSkipped
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_ReadXMaxDimsHorizontal
;
; Read pos and size from first row and then check next rows. Determine the
; highest width of every column and also set item flags to FILLED if any
; widget of a column has this pack mode.
;
; @param X pointer to ctx
; @param Y pointer to widget
; @param r25 number of widgets to handle per row
; @clobbers r16-r20, r24, Y

LayoutCtx_ReadXMaxDimsHorizontal:
  rcall layoutCtxResetAllItems                         ; (R16, R17)
  ldi zl, LOW(layoutCtxCbMaxWidgetTmpToItemHPackIncX)
  ldi zh, HIGH(layoutCtxCbMaxWidgetTmpToItemHPackIncX)
  rjmp LayoutCtx_MultiActionHorizontal                 ; (r16-r20, r24)
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_WriteXDimsContiguous
;
; @param X pointer to ctx
; @param Y pointer to first widget to handle
; @param r25 number of widgets to handle
; @clobbers R16, r18, r19, r24, Z

LayoutCtx_WriteXDimsContiguous:
  ldi zl, LOW(layoutCtxCbCopyItemToWidgetX)
  ldi zh, HIGH(layoutCtxCbCopyItemToWidgetX)
  rjmp layoutCtxForEveryChildContiguous       ; (R16, r18, r19, r24)
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_WriteXDimsSkipped
;
; @param X pointer to ctx
; @param Y pointer to widget
; @param r25 number of widgets to skip in each round

LayoutCtx_WriteXDimsSkipped:
  ldi zl, LOW(layoutCtxCbCopyItemToWidgetX)
  ldi zh, HIGH(layoutCtxCbCopyItemToWidgetX)
  rjmp layoutCtxForEveryChildSkipped
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_ReadYDimsContiguous
;
; @param X pointer to ctx
; @param Y pointer to widget
; @param r25 number of widgets to handle
; @clobbers R16, r18, r19, r24, Z

LayoutCtx_ReadYDimsContiguous:
  ldi zl, LOW(layoutCtxCbCopyWidgetYToItem)
  ldi zh, HIGH(layoutCtxCbCopyWidgetYToItem)
  rjmp layoutCtxForEveryChildContiguous       ; (R16, r18, r19, r24)
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_ReadYDimsSkipped
;
; @param X pointer to ctx
; @param Y pointer to first widget to handle
; @param r25 number of widgets to skip in each round

LayoutCtx_ReadYDimsSkipped:
  ldi zl, LOW(layoutCtxCbCopyWidgetYToItem)
  ldi zh, HIGH(layoutCtxCbCopyWidgetYToItem)
  rjmp layoutCtxForEveryChildSkipped
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_ReadYMaxDimsVertical
;
; Same as LayoutCtx_ReadXMaxDimsContinuous but for height and skipped.
;
; @param X pointer to ctx
; @param Y pointer to widget containing childs to handle
; @param r25 number of widgets in a row
; @clobbers r16-r20, Z

LayoutCtx_ReadYMaxDimsVertical:
  rcall layoutCtxResetAllItems                         ; (R16, R17)
  ldi zl, LOW(layoutCtxCbMaxWidgetTmpToItemVPackNoIncX)
  ldi zh, HIGH(layoutCtxCbMaxWidgetTmpToItemVPackNoIncX) ; (r16-r20)
  rjmp LayoutCtx_MultiActionVertical                     ; (r16-r20)
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxResetAllItems
;
; @param X pointer to ctx
; @clobbers r16, r17

layoutCtxResetAllItems:
  push xl
    push xh
      adiw xh:xl, LAYOUT_CTX_OFFS_NUMITEMS
      ld r17, X+                             ; X now points to first item
      tst r17
      breq layoutCtxResetAllItems_done
layoutCtxResetAllItems_loop:
        rcall layoutCtxResetItem             ; (R16)
        adiw xh:xl, LAYOUT_CTX_ITEM_SIZE
        dec r17
      brne layoutCtxResetAllItems_loop
layoutCtxResetAllItems_done:
    pop xh
  pop xl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxResetItem
;
; @param X pointer to item
; @clobbers r16

layoutCtxResetItem:
  clr r16
  st X+, r16                             ; LAYOUT_CTX_ITEM_OFFS_POS_LO
  st X+, r16                             ; LAYOUT_CTX_ITEM_OFFS_POS_HI
  st X+, r16                             ; LAYOUT_CTX_ITEM_OFFS_SIZE_LO
  st X+, r16                             ; LAYOUT_CTX_ITEM_OFFS_SIZE_HI
  dec r16
  st X, r16                              ; LAYOUT_CTX_ITEM_OFFS_FLAGS (write 0xff)
  sbiw xh:xl, LAYOUT_CTX_ITEM_OFFS_FLAGS
  ret
; @end






; ---------------------------------------------------------------------------
; @routine LayoutCtx_PackYVertical
;
; @param X pointer to ctx
; @param Y pointer to widget containing childs to handle
; @param r25 number of widgets in a row
; @clobbers r16, r18, r19, r24, Z

LayoutCtx_PackYVertical:
  ldi zl, LOW(layoutCtxCbPackYNoIncX)
  ldi zh, HIGH(layoutCtxCbPackYNoIncX)
  rjmp LayoutCtx_MultiActionVertical    ; (r16, r18, r19, r24)
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_PackXHorizontal
;
; @param X pointer to ctx
; @param Y pointer to widget containing childs to handle
; @param r25 number of widgets in a row
; @clobbers r16, r18, r19, r24, Z

LayoutCtx_PackXHorizontal:
  ldi zl, LOW(layoutCtxCbPackXIncX)
  ldi zh, HIGH(layoutCtxCbPackXIncX)
  rjmp LayoutCtx_MultiActionHorizontal  ; (r16, r18, r19)
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_WriteYDimsContiguous
;
; @param X pointer to ctx
; @param Y pointer to first widget to handle
; @param r25 number of widgets to handle
; @clobbers R16, r18, r19, r24, Z

LayoutCtx_WriteYDimsContiguous:
  ldi zl, LOW(layoutCtxCbCopyItemToWidgetYIncX)
  ldi zh, HIGH(layoutCtxCbCopyItemToWidgetYIncX)
  rjmp layoutCtxForEveryChildContiguous       ; (R16, r18, r19, r24)
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_WriteYDimsSkipped
;
; @param X pointer to ctx
; @param Y pointer to first widget to handle
; @param r25 number of widgets to skip in each round

LayoutCtx_WriteYDimsSkipped:
  ldi zl, LOW(layoutCtxCbCopyItemToWidgetYIncX)
  ldi zh, HIGH(layoutCtxCbCopyItemToWidgetYIncX)
  rjmp layoutCtxForEveryChildSkipped       ; (R16, r18, r19)
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_PackXContiguous
;
; @param X pointer to ctx
; @param Y pointer to widget
; @return CFLAG set if more widgets to handle, cleared otherwise
; @return Y pointer to next widget to handle (if CFLAG set)
; @clobbers r16-r21, r24, r25

LayoutCtx_PackXContiguous:
  ldi zl, LOW(layoutCtxCbPackXIncX)
  ldi zh, HIGH(layoutCtxCbPackXIncX)
  rjmp layoutCtxForEveryChildContiguous       ; (r16-r21, r24, r25)
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_PackXSkipped
;
; @param X pointer to ctx
; @param Y pointer to first widget to handle
; @param r25 number of widgets to skip in each round
; @return CFLAG set if more widgets to handle, cleared otherwise
; @return Y pointer to next widget to handle (if CFLAG set)

LayoutCtx_PackXSkipped:
  ldi zl, LOW(layoutCtxCbPackXIncX)
  ldi zh, HIGH(layoutCtxCbPackXIncX)
  rjmp layoutCtxForEveryChildSkipped       ; (R16, r18, r19)
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_PackYContiguous
;
; @param X pointer to ctx
; @param Y pointer to first widget to handle
; @param r25 number of widgets to handle
; @return CFLAG set if more widgets to handle, cleared otherwise
; @return Y pointer to next widget to handle (if CFLAG set)
; @clobbers R16, r18, r19, r24, Z

LayoutCtx_PackYContiguous:
  ldi zl, LOW(layoutCtxCbPackYIncX)
  ldi zh, HIGH(layoutCtxCbPackYIncX)
  rjmp layoutCtxForEveryChildContiguous       ; (R16, r18, r19, r24)
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_PackYSkipped
;
; @param X pointer to ctx
; @param Y pointer to first widget to handle
; @param r25 number of widgets to skip in each round
; @return CFLAG set if more widgets to handle, cleared otherwise
; @return Y pointer to next widget to handle (if CFLAG set)

LayoutCtx_PackYSkipped:
  ldi zl, LOW(layoutCtxCbPackYIncX)
  ldi zh, HIGH(layoutCtxCbPackYIncX)
  rjmp layoutCtxForEveryChildSkipped       ; (R16, r18, r19)
; @end









; ---------------------------------------------------------------------------
; @routine layoutCtxCbCopyWidgetXToItem
;
; @param  Y   widget
; @param  X   item
; @return X   next item
; @clobbers r16

layoutCtxCbCopyWidgetXToItem:
  ldd r16, Y+WIDGET_OFFS_X_LO
  st X+, r16                        ; LAYOUT_CTX_ITEM_OFFS_POS_LO
  ldd r16, Y+WIDGET_OFFS_X_HI
  st X+, r16                        ; LAYOUT_CTX_ITEM_OFFS_POS_HI
  ldd r16, Y+WIDGET_OFFS_TMP_LO
  st X+, r16                        ; LAYOUT_CTX_ITEM_OFFS_SIZE_LO
  ldd r16, Y+WIDGET_OFFS_TMP_HI
  st X+, r16                        ; LAYOUT_CTX_ITEM_OFFS_SIZE_HI
  ldd r16, Y+WIDGET_OFFS_PACK
  andi r16, (1<<WIDGET_PACK_HSELF0_BIT) | (1<<WIDGET_PACK_HSELF1_BIT)
  st X+, r16                        ; LAYOUT_CTX_ITEM_OFFS_FLAGS
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxCbCopyItemToWidgetX
;
; @param  Y   widget
; @param  X   item
; @param  r24 number of items left in layout context (gets decremented)
; @return X   next item
; @clobbers r16

layoutCtxCbCopyItemToWidgetX:
  ld r16, X+                        ; LAYOUT_CTX_ITEM_OFFS_POS_LO
  std Y+WIDGET_OFFS_X_LO, r16
  ld r16, X+                        ; LAYOUT_CTX_ITEM_OFFS_POS_HI
  std Y+WIDGET_OFFS_X_HI, r16
  ld r16, X+                        ; LAYOUT_CTX_ITEM_OFFS_SIZE_LO
  std Y+WIDGET_OFFS_WIDTH_LO, r16
  ld r16, X                         ; LAYOUT_CTX_ITEM_OFFS_SIZE_HI
  std Y+WIDGET_OFFS_WIDTH_HI, r16
  adiw xh:xl, (LAYOUT_CTX_ITEM_SIZE-LAYOUT_CTX_ITEM_OFFS_SIZE_HI)
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxCbCopyWidgetYToItem
;
; @param  Y   widget
; @param  X   item
; @return X   next item
; @clobbers r16

layoutCtxCbCopyWidgetYToItem:
  ldd r16, Y+WIDGET_OFFS_Y_LO
  st X+, r16                        ; LAYOUT_CTX_ITEM_OFFS_POS_LO
  ldd r16, Y+WIDGET_OFFS_Y_HI
  st X+, r16                        ; LAYOUT_CTX_ITEM_OFFS_POS_HI
  ldd r16, Y+WIDGET_OFFS_TMP_LO
  st X+, r16                        ; LAYOUT_CTX_ITEM_OFFS_SIZE_LO
  ldd r16, Y+WIDGET_OFFS_TMP_HI
  st X+, r16                        ; LAYOUT_CTX_ITEM_OFFS_SIZE_HI
  ldd r16, Y+WIDGET_OFFS_PACK
  andi r16, (1<<WIDGET_PACK_VSELF0_BIT) | (1<<WIDGET_PACK_VSELF1_BIT)
  lsr r16
  lsr r16
  st X+, r16                        ; LAYOUT_CTX_ITEM_OFFS_FLAGS
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxCbMaxWidgetTmpToItemHPackNoIncX
;
; @param Y widget
; @param X item
; @clobbers r16-r20

layoutCtxCbMaxWidgetTmpToItemHPackNoIncX:
  ldd r20, Y+WIDGET_OFFS_PACK
  andi r20, (1<<WIDGET_PACK_HSELF0_BIT) | (1<<WIDGET_PACK_HSELF1_BIT)
  rcall layoutCtxCopyWidgetTmpToItemSizeNoIncX
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxCbMaxWidgetTmpToItemHPackIncX
;
; @param Y widget
; @param X item
; @clobbers r16-r20

layoutCtxCbMaxWidgetTmpToItemHPackIncX:
  ldd r20, Y+WIDGET_OFFS_PACK
  andi r20, (1<<WIDGET_PACK_HSELF0_BIT) | (1<<WIDGET_PACK_HSELF1_BIT)
  rcall layoutCtxCopyWidgetTmpToItemSizeNoIncX
  adiw xh:xl, LAYOUT_CTX_ITEM_SIZE
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxCbMaxWidgetTmpToItemVPackNoIncX
;
; @param Y widget
; @param X item
; @clobbers r16-r20

layoutCtxCbMaxWidgetTmpToItemVPackNoIncX:
  ldd r20, Y+WIDGET_OFFS_PACK
  andi r20, (1<<WIDGET_PACK_VSELF0_BIT) | (1<<WIDGET_PACK_VSELF1_BIT)
  lsr r20
  lsr r20
  andi r20, 3
  rcall layoutCtxCopyWidgetTmpToItemSizeNoIncX ; (r16-r19)
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxCbMaxWidgetTmpToItemVPackIncX
;
; @param Y widget
; @param X item
; @clobbers r16-r20

layoutCtxCbMaxWidgetTmpToItemVPackIncX:
  ldd r20, Y+WIDGET_OFFS_PACK
  andi r20, (1<<WIDGET_PACK_VSELF0_BIT) | (1<<WIDGET_PACK_VSELF1_BIT)
  lsr r20
  lsr r20
  rcall layoutCtxCopyWidgetTmpToItemSizeNoIncX ; (r16-r19)
  adiw xh:xl, LAYOUT_CTX_ITEM_SIZE
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxCopyWidgetTmpToItemSizeNoIncX
;
; @param Y widget
; @param X item
; @param r20 widget pack transformed to item flags
; @clobbers r16-r19

layoutCtxCopyWidgetTmpToItemSizeNoIncX:
  ldd r16, Y+WIDGET_OFFS_TMP_LO
  ldd r17, Y+WIDGET_OFFS_TMP_HI

  adiw xh:xl, LAYOUT_CTX_ITEM_OFFS_SIZE_LO
  ld r18, X+                                    ; LAYOUT_CTX_ITEM_OFFS_SIZE_LO
  ld r19, X                                     ; LAYOUT_CTX_ITEM_OFFS_SIZE_HI
  cp r18, r16
  cpc r19, r17
  brcc layoutCtxCopyWidgetTmpToItemSizeNoIncX_checkFlags
  st X, r17                                     ; LAYOUT_CTX_ITEM_OFFS_SIZE_HI
  st -X, r16                                    ; LAYOUT_CTX_ITEM_OFFS_SIZE_LO
  adiw xh:xl, 1                                 ; LAYOUT_CTX_ITEM_OFFS_SIZE_HI
layoutCtxCopyWidgetTmpToItemSizeNoIncX_checkFlags:
  adiw xh:xl, 1                                 ; LAYOUT_CTX_ITEM_OFFS_FLAGS
  ld r17, X                                     ; LAYOUT_CTX_ITEM_OFFS_FLAGS
  cpi r17, 0xff
  breq layoutCtxCopyWidgetTmpToItemSizeNoIncX_storeFlags
  cpi r20, WIDGET_PACK_FILLED
  brne layoutCtxCopyWidgetTmpToItemSizeNoIncX_done
layoutCtxCopyWidgetTmpToItemSizeNoIncX_storeFlags:
  st X, r20
layoutCtxCopyWidgetTmpToItemSizeNoIncX_done:
  sbiw xh:xl, LAYOUT_CTX_ITEM_OFFS_FLAGS
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxCbCopyItemToWidgetYIncX
;
; @param  Y   widget
; @param  X   item
; @return X   pointer to next item
; @clobbers r16

layoutCtxCbCopyItemToWidgetYIncX:
  ld r16, X+                        ; LAYOUT_CTX_ITEM_OFFS_POS_LO
  std Y+WIDGET_OFFS_Y_LO, r16
  ld r16, X+                        ; LAYOUT_CTX_ITEM_OFFS_POS_HI
  std Y+WIDGET_OFFS_Y_HI, r16
  ld r16, X+                        ; LAYOUT_CTX_ITEM_OFFS_SIZE_LO
  std Y+WIDGET_OFFS_HEIGHT_LO, r16
  ld r16, X                         ; LAYOUT_CTX_ITEM_OFFS_SIZE_HI
  std Y+WIDGET_OFFS_HEIGHT_HI, r16
  adiw xh:xl, (LAYOUT_CTX_ITEM_SIZE-LAYOUT_CTX_ITEM_OFFS_SIZE_HI)
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxCbCopyItemToWidgetYNoXInc
;
; @param  Y   widget
; @param  X   item
; @clobbers r16

layoutCtxCbCopyItemToWidgetYNoXInc:
  ld r16, X+                        ; LAYOUT_CTX_ITEM_OFFS_POS_LO
  std Y+WIDGET_OFFS_Y_LO, r16
  ld r16, X+                        ; LAYOUT_CTX_ITEM_OFFS_POS_HI
  std Y+WIDGET_OFFS_Y_HI, r16
  ld r16, X+                        ; LAYOUT_CTX_ITEM_OFFS_SIZE_LO
  std Y+WIDGET_OFFS_HEIGHT_LO, r16
  ld r16, X                         ; LAYOUT_CTX_ITEM_OFFS_SIZE_HI
  std Y+WIDGET_OFFS_HEIGHT_HI, r16
  sbiw xh:xl, LAYOUT_CTX_ITEM_OFFS_SIZE_HI
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxCbPackXIncX
;
; @param Y widget
; @param X item
; @clobbers r16-r21, r25

layoutCtxCbPackXIncX:
  ldd r20, Y+WIDGET_OFFS_TMP_LO
  ldd r21, Y+WIDGET_OFFS_TMP_HI
  rcall layoutCtxPack               ; (r16-r19, r25)
  std Y+WIDGET_OFFS_X_LO, r18
  std Y+WIDGET_OFFS_X_HI, r19
  std Y+WIDGET_OFFS_WIDTH_LO, r20
  std Y+WIDGET_OFFS_WIDTH_HI, r21
  adiw xh:xl, LAYOUT_CTX_ITEM_SIZE
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxCbPackXNoIncX
;
; @param Y widget
; @param X item
; @clobbers r16-r21, r25

layoutCtxCbPackXNoIncX:
  ldd r20, Y+WIDGET_OFFS_TMP_LO
  ldd r21, Y+WIDGET_OFFS_TMP_HI
  rcall layoutCtxPack               ; (r16-r19, r25)
  std Y+WIDGET_OFFS_X_LO, r18
  std Y+WIDGET_OFFS_X_HI, r19
  std Y+WIDGET_OFFS_WIDTH_LO, r20
  std Y+WIDGET_OFFS_WIDTH_HI, r21
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxCbPackYIncX
;
; @param  Y   widget
; @param  X   item
; @return X   next item
; @clobbers r16-r21, r25

layoutCtxCbPackYIncX:
  rcall layoutCtxCbPackYNoIncX
  adiw xh:xl, LAYOUT_CTX_ITEM_SIZE
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxCbPackYNoIncX
;
; @param Y   widget
; @param X   item
; @clobbers r16-r21, r25

layoutCtxCbPackYNoIncX:
  ldd r20, Y+WIDGET_OFFS_TMP_LO
  ldd r21, Y+WIDGET_OFFS_TMP_HI
  rcall layoutCtxPack               ; (r16-r19, r25)
  std Y+WIDGET_OFFS_Y_LO, r18
  std Y+WIDGET_OFFS_Y_HI, r19
  std Y+WIDGET_OFFS_HEIGHT_LO, r20
  std Y+WIDGET_OFFS_HEIGHT_HI, r21
  clc
  ret
; @end




; ---------------------------------------------------------------------------
; @routine Layout_CountChildrenSkipped
;
; @param X pointer to ctx
; @param Y pointer to first widget to handle
; @param r25 number of widgets to skip in each round
; @return r16 number of items in skipped mode (e.g. number of rows)
; @clobbers r18, r19

Layout_CountChildrenSkipped:
  push yl
    push yh
      clr r16
      bigcall OBJ_GetFirstChild
      brcc Layout_CountChildrenSkipped_done
      mov yl, r18
      mov yh, r19
      M_LAYOUT_FOREVERY_SKIPPED layoutCbCount   ; (r18, r19)
Layout_CountChildrenSkipped_done:
    pop yh
  pop yl
  ret
; @end




; ---------------------------------------------------------------------------
; @routine layoutCbCount
;
; @param Y widget
; @clobbers none

layoutCbCount:
  inc r16
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_MultiActionVertical
;
;
; @param X pointer to ctx
; @param Y pointer to widget containing childs to handle
; @param r25 number of widgets in a row
; @param Z routine to call on every child widget
; @clobbers r18, r19, {any}, !r25, !X, !Y

LayoutCtx_MultiActionVertical:
  bigcall OBJ_GetFirstChild
  brcc LayoutCtx_MultiActionVertical_ret
  push yl
    push yh
      push xl
        push xh
          adiw xh:xl, LAYOUT_CTX_OFFS_ITEMS
LayoutCtx_MultiActionVertical_loop:
            mov yl, r18
            mov yh, r19
              rcall layoutForEveryObjectContiguous   ; (r18, r19, {any}, !r25)
            bigcall OBJ_GetNext
            brcc LayoutCtx_MultiActionVertical_done
            adiw xh:xl, LAYOUT_CTX_ITEM_SIZE
          rjmp LayoutCtx_MultiActionVertical_loop
LayoutCtx_MultiActionVertical_done:
        pop xh
      pop xl
    pop yh
  pop yl  
LayoutCtx_MultiActionVertical_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine LayoutCtx_MultiActionHorizontal
;
;
; @param X pointer to ctx
; @param Y pointer to widget containing children to work on
; @param r25 number of widgets to handle per row
; @param Z routine to call on every child widget
; @clobbers r16, r18, r19, r24

LayoutCtx_MultiActionHorizontal:
  bigcall OBJ_GetFirstChild
  brcc LayoutCtx_MultiActionHorizontal_ret
  push yl
    push yh
      push xl
        push xh
          adiw xh:xl, LAYOUT_CTX_OFFS_ITEMS
LayoutCtx_MultiActionHorizontal_loop:
            mov yl, r18
            mov yh, r19
              push xl                                    ; first item
                push xh
                  rcall layoutForEveryObjectContiguous   ; (r18, r19, Y {any})
                pop xh
              pop xl                                     ; first item
            bigcall OBJ_GetNext
            brcc LayoutCtx_MultiActionHorizontal_done
          rjmp LayoutCtx_MultiActionHorizontal_loop
LayoutCtx_MultiActionHorizontal_done:
        pop xh
      pop xl
    pop yh
  pop yl  
LayoutCtx_MultiActionHorizontal_ret:
  ret
; @end






; ---------------------------------------------------------------------------
; @routine layoutCtxForEveryChildContiguous
;
; @param X pointer to ctx
; @param Y pointer to first widget to handle
; @param Z routine to call
; @clobbers r18, r19, r24, {any}, !r25, !X, !Y

layoutCtxForEveryChildContiguous:
  bigcall OBJ_GetFirstChild
  brcc layoutCtxForEveryChildContiguous_ret
  push yl
    push yh
      mov yl, r18
      mov yh, r19
      push xl
        push xh
          push r25
            adiw xh:xl, LAYOUT_CTX_OFFS_NUMITEMS
            ld r24, X+                              ; X now points to first item
            clr r25
            rcall layoutForEveryObjectContiguous    ; (r18, r19, Y {any})
          pop r25
        pop xh
      pop xl
    pop yh
  pop yl
layoutCtxForEveryChildContiguous_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutCtxForEveryChildSkipped
;
; @param X   pointer to ctx
; @param Y   pointer to first widget to handle
; @param Z   routine to call
; @param r25 number of widgets per row
; @clobbers any, !r25, !X, !Y

layoutCtxForEveryChildSkipped:
  bigcall OBJ_GetFirstChild
  brcc layoutCtxForEveryChildSkipped_ret
  push yl
    push yh
      mov yl, r18
      mov yh, r19
      push xl
        push xh
          push r25
            adiw xh:xl, LAYOUT_CTX_OFFS_NUMITEMS
            ld r24, X+                                             ; X now points to first item
            rcall layoutForEveryObjectSkipped
          pop r25
        pop xh
      pop xl
    pop yh
  pop yl
layoutCtxForEveryChildSkipped_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutForEveryObjectContiguous
;
; This routine itself uses R18, R19, R25 and Y. All other registers may be
; passed to the callback routine.
; However, the callback function must make sure that it doesn't modify registers
; which are to be passed to next objects later!
;
; This routine traverses the given number of contiguous objects until there is no
; next object.
;
; Y and R25 are preserved around the callback. Z MUST NOT be modified by the callback!
;
; @param  Y       object to start with
; @param  r25     number of widgets to handle (0=all)
; @param  Z       pointer to routine to call (using ICALL)
; @return CFLAG   set if the callback returned with CFLAG set on any widget
; @return Y       object on which the call returned with CFLAG set (otherwise last object handled)
; @clobbers       r18, r19, Y {any}

layoutForEveryObjectContiguous:
  push r25
    mov r18, r25
layoutForEveryObjectContiguous_loop:
      push r18
        push r25
          push yl
            push yh
              icall
            pop yh
          pop yl
        pop r25
      pop r18
      brcs layoutForEveryObjectContiguous_ret
      tst r18
      breq layoutForEveryObjectContiguous_next
      dec r18
      clc
      breq layoutForEveryObjectContiguous_ret
layoutForEveryObjectContiguous_next:
      push r18
        bigcall OBJ_GetNext
        brcs layoutForEveryObjectContiguous_loopEnd
      pop r18
      rjmp layoutForEveryObjectContiguous_ret
layoutForEveryObjectContiguous_loopEnd:
        mov yl, r18
        mov yh, r19
      pop r18
    rjmp layoutForEveryObjectContiguous_loop
layoutForEveryObjectContiguous_ret:
  pop r25
  ret
; @end



; ---------------------------------------------------------------------------
; @routine layoutForEveryObjectSkipped
;
; This routine itself uses R18, R19, R25 and Y. All other registers may be
; passed to the callback routine.
; However, the callback function must make sure that it doesn't modify registers
; which are to be passed to next objects later!
;
; This routine traverses the given objects skipping [R25] objects after each round
; until there is no next object.
;
; Y and R25 are preserved around the callback. Z MUST NOT be modified by the callback!
;
; @param  Y       object to start with
; @param  r25     number of widgets to skip per round
; @param  Z       pointer to routine to call (using ICALL)
; @return CFLAG   set if the callback returned with CFLAG set on any widget
; @return Y       object on which the call returned with CFLAG set (garbage otherwise)
; @clobbers       r18, r19, Y {any}

layoutForEveryObjectSkipped:
layoutForEveryObjectSkipped_loop:
    push r25
      push yl
        push yh
          icall
        pop yh
      pop yl
    pop r25
    brcs layoutForEveryObjectSkipped_ret
    push r16
      mov r16, r25
      bigcall OBJ_SkipObjects              ; (R16)
    pop r16
    brcc layoutForEveryObjectSkipped_ret
    mov yl, r18
    mov yh, r19
  rjmp layoutForEveryObjectSkipped_loop
layoutForEveryObjectSkipped_ret:
  ret
; @end




; ---------------------------------------------------------------------------
; @routine Layout_SetDefaultWidths
;
; Set default width in WIDGET_OFFS_TMP_LO/HI of child widgets
;
; @param Y pointer to widget
; @clobbers any, !Y

Layout_SetDefaultWidths:
  push yl
    push yh
      bigcall OBJ_GetFirstChild
      brcc Layout_SetDefaultWidths_ret
Layout_SetDefaultWidths_loop:
        mov yl, r18
        mov yh, r19
        bigcall Widget_GetDefaultWidth
        std Y+WIDGET_OFFS_TMP_LO, r18
        std Y+WIDGET_OFFS_TMP_HI, r19
        bigcall OBJ_GetNext
      brcs Layout_SetDefaultWidths_loop
Layout_SetDefaultWidths_ret:
    pop yh
  pop yl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Layout_SetDefaultHeights
;
; Set default height in WIDGET_OFFS_TMP_LO/HI of child widgets
;
; @param Y pointer to widget
; @clobbers any, !Y

Layout_SetDefaultHeights:
  bigcall OBJ_GetFirstChild
  brcc Layout_SetDefaultHeights_ret
  push yl
    push yh
Layout_SetDefaultHeights_loop:
        mov yl, r18
        mov yh, r19
        bigcall Widget_GetDefaultHeight
        std Y+WIDGET_OFFS_TMP_LO, r18
        std Y+WIDGET_OFFS_TMP_HI, r19
        bigcall OBJ_GetNext
      brcs Layout_SetDefaultHeights_loop
    pop yh
  pop yl
Layout_SetDefaultHeights_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Layout_SumTmpValues
;
; Ignores invisible widgets.
;
; @param Y pointer to widget
; @return r19:r18 total size of all child widgets plus space between
; @clobbers r16, r17, r18, r19, r20, r21, r22, r23, Z

Layout_SumTmpValues:
  ldd zl, Y+WIDGET_OFFS_STYLE_LO
  ldd zh, Y+WIDGET_OFFS_STYLE_HI

  ; get spacing
  adiw zh:zl, WIDGET_STYLE_OFFS_SPACING
  lpm r22, Z
  sbiw zh:zl, WIDGET_STYLE_OFFS_SPACING
  ; get outer border
  adiw zh:zl, WIDGET_STYLE_OFFS_OUTERBORDERSIZE
  lpm r23, Z
  sbiw zh:zl, WIDGET_STYLE_OFFS_OUTERBORDERSIZE
  ; start adding
  clr r20
  clr r21
  push yl
    push yh
      bigcall OBJ_GetFirstChild
      brcc Layout_SumTmpValues_finish
Layout_SumTmpValues_loop:
        mov yl, r18
        mov yh, r19
        ldd r18, Y+OBJECT_OFFS_FLAGS
        sbrs r18, WIDGET_FLAGS_VISIBLE_BIT
          rjmp Layout_SumTmpValues_next
        ldd r18, Y+WIDGET_OFFS_TMP_LO
        ldd r19, Y+WIDGET_OFFS_TMP_HI
        add r20, r18    ; add widget size
        adc r21, r19
        add r20, r22    ; add spacing
        adc r21, r22
        sub r21, r22
Layout_SumTmpValues_next:
        bigcall OBJ_GetNext
      brcs Layout_SumTmpValues_loop
Layout_SumTmpValues_loopEnd:
    mov r16, r20
    or r16, r21
    breq Layout_SumTmpValues_finish
    sub r20, r22   ; sub last spacing
    sbc r21, r22
    add r21, r22
Layout_SumTmpValues_finish:
    add r20, r23   ; add outer border (begin)
    adc r21, r23
    sub r21, r23
    add r20, r23   ; add outer border (end)
    adc r21, r23
    sub r21, r23
Layout_SumTmpValues_done:
    mov r18, r20
    mov r19, r21
    pop yh
  pop yl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Layout_GetMaxTmp
;
; Ignores invisible widgets.
;
; @param Y pointer to widget
; @return r19:r18 maximum value in WIDGET_OFFS_TMP_LO/HI of children
; @clobbers r20, r21

Layout_GetMaxTmp:
  clr r20
  clr r21
  push yl
    push yh
      bigcall OBJ_GetFirstChild
Layout_GetMaxTmp_loop:
        brcc Layout_GetMaxTmp_ret
        mov yl, r18
        mov yh, r19
        ldd r18, Y+OBJECT_OFFS_FLAGS
        sbrs r18, WIDGET_FLAGS_VISIBLE_BIT
          rjmp Layout_GetMaxTmp_next
        ldd r18, Y+WIDGET_OFFS_TMP_LO
        ldd r19, Y+WIDGET_OFFS_TMP_HI
        ; max
        cp r20, r18
        cpc r21, r19
        brcc Layout_GetMaxTmp_next
        mov r20, r18
        mov r21, r19
Layout_GetMaxTmp_next:
        bigcall OBJ_GetNext
      rjmp Layout_GetMaxTmp_loop
Layout_GetMaxTmp_ret:
      mov r18, r20
      mov r19, r21
    pop yh
  pop yl
  ret
; @end




#endif
