; ***************************************************************************
; 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_WIDGET_ASM
#define AQH_AVR_GUI2_WIDGET_ASM


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

; Widget in flash
.equ WIDGET_OFFS_WIDGET      =  OBJECT_SIZE
.equ WIDGET_OFFS_OPTS_LO     =  WIDGET_OFFS_WIDGET+0
.equ WIDGET_OFFS_OPTS_HI     =  WIDGET_OFFS_WIDGET+1
.equ WIDGET_OFFS_X_LO        =  WIDGET_OFFS_WIDGET+2
.equ WIDGET_OFFS_X_HI        =  WIDGET_OFFS_WIDGET+3
.equ WIDGET_OFFS_Y_LO        =  WIDGET_OFFS_WIDGET+4
.equ WIDGET_OFFS_Y_HI        =  WIDGET_OFFS_WIDGET+5
.equ WIDGET_OFFS_WIDTH_LO    =  WIDGET_OFFS_WIDGET+6
.equ WIDGET_OFFS_WIDTH_HI    =  WIDGET_OFFS_WIDGET+7
.equ WIDGET_OFFS_HEIGHT_LO   =  WIDGET_OFFS_WIDGET+8
.equ WIDGET_OFFS_HEIGHT_HI   =  WIDGET_OFFS_WIDGET+9
.equ WIDGET_OFFS_FRONTCOL_LO =  WIDGET_OFFS_WIDGET+10
.equ WIDGET_OFFS_FRONTCOL_HI =  WIDGET_OFFS_WIDGET+11
.equ WIDGET_OFFS_BACKCOL_LO  =  WIDGET_OFFS_WIDGET+12
.equ WIDGET_OFFS_BACKCOL_HI  =  WIDGET_OFFS_WIDGET+13
.equ WIDGET_OFFS_FONT_LO     =  WIDGET_OFFS_WIDGET+14
.equ WIDGET_OFFS_FONT_HI     =  WIDGET_OFFS_WIDGET+15
.equ WIDGET_OFFS_SDRAM_LO    =  WIDGET_OFFS_WIDGET+16
.equ WIDGET_OFFS_SDRAM_HI    =  WIDGET_OFFS_WIDGET+17
.equ WIDGET_SIZE             =  WIDGET_OFFS_WIDGET+18

; SDRAM data for widget
.equ WIDGET_SD_OFFS_FLAGS     = 0
.equ WIDGET_SD_SIZE           = 1

; widget opts_lo
.equ WIDGET_OPTSLO_BORDER_BIT  = 7
.equ WIDGET_OPTSLO_INPUT_BIT   = 6 ; TOUCH, KEY
.equ WIDGET_OPTSLO_TIMER_BIT   = 5
.equ WIDGET_OPTSLO_MSGRECV_BIT = 4

.equ WIDGET_OPTSHI_HALIGNRIGHT_BIT   = 7
.equ WIDGET_OPTSHI_HALIGNCENTER_BIT  = 6
.equ WIDGET_OPTSHI_VALIGNBOTTOM_BIT  = 5
.equ WIDGET_OPTSHI_VALIGNCENTER_BIT  = 4

; widget flags
.equ WIDGET_FLAGS_ACTIVE_BIT  = 7
.equ WIDGET_FLAGS_TOUCH_BIT   = 6
.equ WIDGET_FLAGS_DIRTY_BIT   = 5
.equ WIDGET_FLAGS_PRESSED_BIT = 4

; SDRAM data for signal WIDGET_SIGNAL_TOUCH
.equ WIDGET_DATA_TOUCH_OFFS_X_LO  = 0
.equ WIDGET_DATA_TOUCH_OFFS_X_HI  = 1
.equ WIDGET_DATA_TOUCH_OFFS_Y_LO  = 2
.equ WIDGET_DATA_TOUCH_OFFS_Y_HI  = 3
.equ WIDGET_DATA_TOUCH_OFFS_STATE = 4
.equ WIDGET_DATA_TOUCH_SIZE       = 5

; signals
.equ WIDGET_SIGNAL_SHOW       =  OBJECT_SIGNAL_NEXTFREE+0
.equ WIDGET_SIGNAL_HIDE       =  OBJECT_SIGNAL_NEXTFREE+1
.equ WIDGET_SIGNAL_DRAW       =  OBJECT_SIGNAL_NEXTFREE+2
.equ WIDGET_SIGNAL_TOUCH      =  OBJECT_SIGNAL_NEXTFREE+3
.equ WIDGET_SIGNAL_CLICKED    =  OBJECT_SIGNAL_NEXTFREE+4
.equ WIDGET_SIGNAL_NEXTFREE   =  OBJECT_SIGNAL_NEXTFREE+5


; args to Widget_AlignPos
.equ WIDGET_1DALIGN_END_BIT    = 0
.equ WIDGET_1DALIGN_CENTER_BIT = 1
.equ WIDGET_1DALIGN_BORDER_BIT = 2



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

.cseg



; ---------------------------------------------------------------------------
; @routine Widget_Init @global
;
; @param Z byte address of widget object (for LPM!)

Widget_Init:
  rcall Widget_GetSdramPtr        ; (none)
  clr r16
  ori r16, (1<<WIDGET_FLAGS_DIRTY_BIT)
  std Y+WIDGET_SD_OFFS_FLAGS, r16
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_OnCreate @global
;
; @param Z byte address of widget object (for LPM!)
; @return CFLAG set if signal handled
; @clobbers any, !Z

Widget_OnCreate:
  rcall Widget_Init
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_OnDraw @global
;
; @param Z byte address of widget object (for LPM!)
; @param xl param1
; @param xh param2
; @return CFLAG set if signal handled
; @clobbers any, !Z

Widget_OnDraw:
  rcall Widget_GetSdramPtr
  ldd r17, Y+WIDGET_SD_OFFS_FLAGS

  ; check whether widget is active
  clc
  sbrs r17, WIDGET_FLAGS_ACTIVE_BIT
    rjmp Widget_OnDraw_ret

  ; check whether widget is dirty
  sec
  sbrs r17, WIDGET_FLAGS_DIRTY_BIT
    rjmp Widget_OnDraw_ret

  ; clear dirty bit
  cbr r17, (1<<WIDGET_FLAGS_DIRTY_BIT)
  std Y+WIDGET_SD_OFFS_FLAGS, r17
  ; clear widget background
  rcall Widget_Clear

Widget_OnDraw_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_OnDrawNop @global
;
; this routine only clears the DIRTY bit
; 
; @param Z byte address of widget object (for LPM!)
; @param xl param1
; @param xh param2
; @return CFLAG set if signal handled
; @clobbers any, !Z

Widget_OnDrawNop:
  rcall Widget_GetSdramPtr
  ldd r17, Y+WIDGET_SD_OFFS_FLAGS
  cbr r17, (1<<WIDGET_FLAGS_DIRTY_BIT)
  std Y+WIDGET_SD_OFFS_FLAGS, r17
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_IsPointInRect @global
;
; @param  R19:R18 check X
; @param  R21:R20 check Y
; @param  R5:R4   widget X
; @param  R7:R6   widget Y
; @param  R9:R8   widget width
; @param  R11:R10 widget height
; @return CFLAG set if inside rect, cleared otherwise
; @clobbers R16, R17

Widget_IsPointInRect:
  ; check X
  mov r16, r18    ; checkX-widgetX
  mov r17, r19
  sub r16, r4
  sbc r17, r5
  brcs Widget_IsPointInRect_no   ; jmp if left of widget
  sub r16, r8     ; sub width
  sbc r17, r9
  brcc Widget_IsPointInRect_no   ; jmp if right of widget

  mov r16, r20    ; checkY-widgetY
  mov r17, r21
  sub r16, r6
  sbc r17, r7
  brcs Widget_IsPointInRect_no   ; jmp if above widget
  sub r16, r10    ; sub height
  sbc r17, r11
  brcc Widget_IsPointInRect_no   ; jmp if below widget
  
  sec             ; is inside rect
  rjmp Widget_IsPointInRect_ret

Widget_IsPointInRect_no:
  clc
Widget_IsPointInRect_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_LoadDisplayRegs @global
;
; @param   Z       byte address of widget object (for LPM!)
; @return  R1:R0   background color
; @return  R3:R2   foreground color
; @return  R5:R4   X
; @return  R7:R6   Y
; @return  R9:R8   widget width
; @return  R11:R10 widget height
; @clobbers none

Widget_LoadDisplayRegs:
  push zl
    push zh
      adiw zh:zl, WIDGET_OFFS_X_LO
      lpm r4, Z+    ; X_LO
      lpm r5, Z+
      lpm r6, Z+    ; Y_LO
      lpm r7, Z+
      lpm r8, Z+    ; WIDTH_LO
      lpm r9, Z+
      lpm r10, Z+   ; HEIGHT_LO
      lpm r11, Z+
      lpm r2, Z+    ; FRONTCOL_LO
      lpm r3, Z+
      lpm r0, Z+    ; BACKCOL_LO
      lpm r1, Z+
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_GetSdramPtr @global
;
; @param  Z  byte address of widget object (for LPM!)
; @return Y  pointer to SDRAM data for widget
; @clobbers none

Widget_GetSdramPtr:
  adiw zh:zl, WIDGET_OFFS_SDRAM_LO
  lpm yl, Z+
  lpm yh, Z
  sbiw zh:zl, WIDGET_OFFS_SDRAM_LO+1
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_GetRootWidget @global
;
; @param  Z  byte address of object (for LPM!)
; @return Z  byte address pointer to RootWidget
; @clobbers r18, r19

Widget_GetRootWidget:
  rcall OBJ_IsObject                      ; (none)
  brcc Widget_GetRootWidget_end
Widget_GetRootWidget_loop:
    bigcall OBJ_GetParent                 ; (none)
    brcc Widget_GetRootWidget_found       ; no parent, return current Z
    mov zl, r18
    mov zh, r19
  rjmp Widget_GetRootWidget_loop
Widget_GetRootWidget_found:
  sec
Widget_GetRootWidget_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_GetApp @global
;
; @param  Z       byte address of object (for LPM!)
; @return R19:R18 byte address pointer to GuiApp
; @clobbers (r18, r19)

Widget_GetApp:
  rcall OBJ_IsObject                      ; (none)
  brcc Widget_GetApp_ret
  push zl
    push zh
      rcall Widget_GetRootWidget          ; Z:=rootWidget (r18, r19)
      brcc Widget_GetApp_popRet
      rcall RootWidget_GetApp             ; R19:R18=app (none)
Widget_GetApp_popRet:
    pop zh
  pop zl  
Widget_GetApp_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_GrabTouchEvents @global
;
; Let this widget grab touch events until ungrabbed.
;
; @param  Z byte address of object (for LPM!)
; @return CFLAG set if grabbed, cleared on error
; @clobbers X, Y (r16, r17, r18, r19)

Widget_GrabTouchEvents:
  push zl
    push zh
      mov xl, zl
      mov xh, zh
      rcall Widget_GetApp                  ; R19:R18:=app
      brcc Widget_GrabTouchEvents_popRet
      mov zl, r18
      mov zh, r19
      bigcall GuiApp_GrabTouchEvents       ; (R16, R17)
Widget_GrabTouchEvents_popRet:
    pop zh
  pop zl
  ret
; @end




; ---------------------------------------------------------------------------
; @routine Widget_UngrabTouchEvents @global
;
; Let this widget un-grab touch events.
;
; @param  Z byte address of object (for LPM!)
; @return CFLAG set if ungrabbed, cleared on error
; @clobbers X (r16, r17, r18, r19)

Widget_UngrabTouchEvents:
  push zl
    push zh
      mov xl, zl
      mov xh, zh
      rcall Widget_GetApp                  ; R19:R18:=app
      brcc Widget_UngrabTouchEvents_popRet
      mov zl, r18
      mov zh, r19
      bigcall GuiApp_UngrabTouchEvents       ; (R16, R17)
Widget_UngrabTouchEvents_popRet:
    pop zh
  pop zl
  ret
; @end




; ---------------------------------------------------------------------------
; @routine Widget_AddFlagsDown @global
;
; @param   Z       byte address of widget object (for LPM!)
; @param   R16     flags to add
; @clobbers R17, R18, R19, R22, R23, Y

Widget_AddFlagsDown:
  rcall OBJ_IsObject
  brcc Widget_AddFlagsDown_ret

  rcall Widget_AddFlags                   ; (R17, R22, R23)
  push zl
    push zh
      bigcall OBJ_GetFirstChild           ; R19:R18=next object
Widget_AddFlagsDown_loop:
        brcc Widget_AddFlagsDown_loopEnd
        mov zl, r18
        mov zh, r19
        rcall Widget_AddFlagsDown
        rcall OBJ_GetNext
      rjmp Widget_AddFlagsDown_loop
      
Widget_AddFlagsDown_loopEnd:
    pop zh
  pop zl
Widget_AddFlagsDown_ret:
  ret
; @end




; ---------------------------------------------------------------------------
; @routine Widget_SubFlagsDown @global
;
; @param   Z       byte address of widget object (for LPM!)
; @param   R16     flags to add
; @clobbers R17, R18, R19, R22, R23

Widget_SubFlagsDown:
  rcall OBJ_IsObject                      ; (none)
  brcc Widget_SubFlagsDown_ret
  rcall Widget_SubFlags                   ; (R17)
  push zl
    push zh
      bigcall OBJ_GetFirstChild           ; R19:R18=next object
Widget_SubFlagsDown_loop:
        brcc Widget_SubFlagsDown_loopEnd
        mov zl, r18
        mov zh, r19
        rcall Widget_SubFlagsDown
        rcall OBJ_GetNext
      rjmp Widget_SubFlagsDown_loop
      
Widget_SubFlagsDown_loopEnd:
    pop zh
  pop zl
Widget_SubFlagsDown_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_AddFlags @global
;
; @param   Z       byte address of widget object (for LPM!)
; @param   R16     flags to add
; @clobbers R17, R22, R23

Widget_AddFlags:
  rcall OBJ_IsObject
  brcc Widget_AddFlags_ret

  push yl
    push yh
      rcall Widget_GetSdramPtr                ; (none)
      ldd r17, Y+WIDGET_SD_OFFS_FLAGS
      or r17, r16
      std Y+WIDGET_SD_OFFS_FLAGS, r17
    pop yh
  pop yl
Widget_AddFlags_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_SubFlags @global
;
; @param   Z       byte address of widget object (for LPM!)
; @param   R16     flags to clear
; @clobbers R17

Widget_SubFlags:
  mov r17, zl
  or r17, zh
  breq Widget_SubFlags_ret
  
  push yl
    push yh
      rcall Widget_GetSdramPtr                ; (none)
      ldd r17, Y+WIDGET_SD_OFFS_FLAGS
      com r16                                 ; one's-complement
      and r17, r16
      com r16
      std Y+WIDGET_SD_OFFS_FLAGS, r17
    pop yh
  pop yl
Widget_SubFlags_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_Clear @global
;
; @param Z byte address of widget object (for LPM!)

Widget_Clear:
  rcall OBJ_IsObject
  brcc Widget_Clear_ret

  rcall widgetLoadCoordsForFullWidget
  rcall Widget_MakeAbsPos             ; (R16, R17)
  rcall Widget_GetSdramPtr            ; (none)
  rcall Widget_SelectColors           ; (R16)
  mov r2, r0                          ; use background
  mov r3, r1
  bigcall Display_FillRect
Widget_Clear_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_DrawBorder @global
;
; @param Z byte address of widget object (for LPM!)
; @param r3:r2 color
; @clobbers any, !Z

Widget_DrawBorder:
  rcall OBJ_IsObject
  brcc Widget_DrawBorder_ret
  rcall widgetLoadCoordsForFullWidget
  rcall Widget_MakeAbsPos         ; (R16, R17)
  bigcall Display_DrawRect
Widget_DrawBorder_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_SelectColors @global
;
; @param  Z     byte address of widget object (for LPM!)
; @param  Y     pointer to Widget data in SDRAM
; @return r1:r0 background color
; @return r3:r2 foreground color
; @clobbers R16

Widget_SelectColors:
  ldd r16, Y+WIDGET_SD_OFFS_FLAGS ; flags
  andi r16, (1<<WIDGET_FLAGS_PRESSED_BIT)
  brne Widget_SelectColors_pressed
  push zl
    push zh
      adiw zh:zl, WIDGET_OFFS_FRONTCOL_LO
      lpm r2, Z+
      lpm r3, Z+
      lpm r0, Z+
      lpm r1, Z
    pop zh
  pop zl
  rjmp Widget_SelectColors_ret

Widget_SelectColors_pressed:
  ; background color
  ldi r16, LOW(STYLE_BUTTON_COL_BG_PRESSED)
  mov r0, r16
  ldi r16, HIGH(STYLE_BUTTON_COL_BG_PRESSED)
  mov r1, r16

  ; foreground color
  ldi r16, LOW(STYLE_BUTTON_COL_FG_PRESSED)
  mov r2, r16
  ldi r16, HIGH(STYLE_BUTTON_COL_FG_PRESSED)
  mov r3, r16

Widget_SelectColors_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_MakeAbsPos @global
;
; @param Z byte address of widget object (for LPM!)
; @param r5:r4   X
; @param r7:r6   Y
; @clobbers r16, r17

Widget_MakeAbsPos:
  push zl
    push zh
      push r18
        push r19
Widget_MakeAbsPos_loop:
            adiw zh:zl, WIDGET_OFFS_X_LO
            lpm r16, Z+
            lpm r17, Z+
            add r4, r16  ; adjust X
            adc r5, r17
            lpm r16, Z+
            lpm r17, Z
            add r6, r16  ; adjust Y
            adc r7, r17
            sbiw zh:zl, WIDGET_OFFS_X_LO+3
            rcall OBJ_GetParent
            brcc Widget_MakeAbsPos_loopEnd
            mov zl, r18
            mov zh, r19
          rjmp Widget_MakeAbsPos_loop
Widget_MakeAbsPos_loopEnd:
        pop r19
      pop r18
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_FillRect @global
;
; @param Z byte address of widget object (for LPM!)
; @param r3:r2 color
; @param r5:r4   X0
; @param r7:r6   Y0
; @param r9:r8   X1/W
; @param r11:r10 Y1/H
; @clobbers any, !Z

Widget_FillRect:
  rcall Widget_MakeAbsPos ; (R16, R17)
  bigcall Display_FillRect ; directly call graphics function
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_DrawImage @global
;
; @param  Z       byte address of widget object (for LPM!)
; @param  R1:R0   background color
; @param  R5:R4   X relative to widget
; @param  R7:R6   Y relative to widget
; @param  R13:R12 byte address pointer to image in FLASH (for LPM!)

Widget_DrawImage:
  rcall widgetCalcAbsPosAndBorders
  push zl
    push zh
      mov zl, r12
      mov zh, r13
      bigcall ili9341ImageDraw
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_DrawTextFlash @global
;
; @param  Z       byte address of widget object (for LPM!)
; @param  R5:R4   X relative to widget
; @param  R7:R6   Y relative to widget
; @param  R13:R12 byte address pointer to text in flash (for LPM!)
; @clobbers any, !Z

Widget_DrawTextFlash:
  rcall Widget_GetSdramPtr        ; (none)
  rcall Widget_SelectColors
  rjmp Widget_DrawColoredTextFlash
; @end



; ---------------------------------------------------------------------------
; @routine Widget_DrawColoredTextFlash @global
;
; @param  Z       byte address of widget object (for LPM!)
; @param  R1:R0   background color
; @param  R3:R2   foreground color
; @param  R5:R4   X relative to widget
; @param  R7:R6   Y relative to widget
; @param  R13:R12 byte address pointer to text in flash (for LPM!)
; @clobbers any, !Z

Widget_DrawColoredTextFlash:
  rcall widgetCalcAbsPosAndBorders
Widget_DrawColoredTextFlash_loop:
    push zl
      push zh
        mov zl, r12
        mov zh, r13
        lpm r16, Z
      pop zh
    pop zl
    tst r16
    breq Widget_DrawColoredTextFlash_loopEnd
    rcall widgetDrawChar
    brcc Widget_DrawColoredTextFlash_loopEnd
    ldi r16, 1
    add r12, r16
    adc r13, r12
    sub r13, r12
  rjmp Widget_DrawColoredTextFlash_loop
Widget_DrawColoredTextFlash_loopEnd:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_DrawTextSDRAM @global
;
; @param  Z       byte address of widget object (for LPM!)
; @param  R5:R4   X relative to widget
; @param  R7:R6   Y relative to widget
; @param  X       pointer to text in SDRAM
; @clobbers any, !Z

Widget_DrawTextSDRAM:
  rcall Widget_GetSdramPtr        ; (none)
  rcall Widget_SelectColors       ; (R16)
  rjmp Widget_DrawColoredTextSDRAM
; @end



; ---------------------------------------------------------------------------
; @routine Widget_DrawColoredTextSDRAM @global
;
; @param  Z       byte address of widget object (for LPM!)
; @param  R1:R0   background color
; @param  R3:R2   foreground color
; @param  R5:R4   X relative to widget
; @param  R7:R6   Y relative to widget
; @param  X       pointer to text in SDRAM
; @clobbers any, !Z

Widget_DrawColoredTextSDRAM:
  rcall widgetCalcAbsPosAndBorders           ; (R16, R17)
Widget_DrawColoredTextSDRAM_loop:
    ld r16, X
    tst r16
    breq Widget_DrawColoredTextSDRAM_loopEnd
    push xl
      push xh
        rcall widgetDrawChar                 ; (any, !Y, !R6, !R7, !R8, !R9, !R10, !R11)
      pop xh
    pop xl
    brcc Widget_DrawColoredTextSDRAM_loopEnd
    adiw xh:xl, 1
  rjmp Widget_DrawColoredTextSDRAM_loop
Widget_DrawColoredTextSDRAM_loopEnd:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine widgetCalcAbsPosAndBorders
;
; @param  Z       byte address of widget object (for LPM!)
; @param  R5:R4   X relative to widget
; @param  R7:R6   Y relative to widget
; @return R9:R8   first X pos right of widget
; @return R5:R4   X relative to screen
; @return R7:R6   Y relative to screen
; @return R9:R8   first X pos right of widget (abs)
; @return R11:R10 first Y pos below widget (abs)
; @clobbers r16, r17

widgetCalcAbsPosAndBorders:
  push zl
    push zh
      rcall Widget_MakeAbsPos       ; (R16, R17)
      adiw zh:zl, WIDGET_OFFS_WIDTH_LO
      lpm r8, Z+    ; WIDTH_LO
      lpm r9, Z+
      add r8, r4    ; convert width to first pos right of widget
      adc r9, r5
      lpm r10, Z+   ; HEIGHT_LO
      lpm r11, Z+
      add r10, r6   ; convert height to first pos below widget
      adc r11, r7
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine widgetDrawChar
;
; @param  Z       byte address of widget object (for LPM!)
; @param  R16     char to write
; @param  R5:R4   absolute X on screen
; @param  R7:R6   absolute Y on screen
; @param  R9:R8   first X pos right of windows
; @return CFLAG set if char printed, cleared otherwise
; @return R5:R4   X pos behind char
; @clobbers any, !Y, !R6, !R7, !R8, !R9, !R10, !R11

widgetDrawChar:
  push zl
    push zh
      ; save R16 (char to write)
      mov r14, r16
      
      ; get font to Z
      adiw zh:zl, WIDGET_OFFS_FONT_LO
      lpm r18, Z+
      lpm zh, Z
      mov zl, r18
      or r18, zh
      clc
      breq widgetDrawChar_done

      ; check whether the char fits inside window
      bigcall FONT_GetCharWidth         ; r16=char width
      
      clr r17
      add r16, r4                       ; char width+X
      adc r17, r5
      sub r16, r8                       ; check against window width
      sbc r17, r9
      brcc widgetDrawChar_done          ; not fit, jmp
    
      ; actually draw char
      mov r16, r14
      push r8
        push r9
          push r10
            push r11
              bigcall Display_DrawChar
            pop r11
          pop r10
        pop r9
      pop r8
      clr r16
      add r4, r18                       ; increment X
      adc r5, r16
      sec                               ; write succeeded
widgetDrawChar_done:
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_Tree_Init @global
;
; @param Z byte address of root widget object (for LPM!)

Widget_Tree_Init:
  push zl
    push zh
Widget_Tree_Init_loop:
        mov r16, zl
        or r16, zh
        breq Widget_Tree_Init_loopEnd
        rcall Widget_Init
        bigcall OBJ_GetBelow
        mov zl, r18
        mov zh, r19
      rjmp Widget_Tree_Init_loop
Widget_Tree_Init_loopEnd:
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_Tree_SendDrawSignal @global
;
; @param Z byte address of root widget object (for LPM!)

Widget_Tree_SendDrawSignal:
  ldi r16, WIDGET_SIGNAL_DRAW
  clr r17
  ; only let active and dirty widgets draw
  ldi r20, (1<<WIDGET_FLAGS_DIRTY_BIT)
  ldi r21, (1<<WIDGET_FLAGS_DIRTY_BIT)
  rcall Widget_TreeMatchFlagsAndSendSignalToActive
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_Tree_SendTouchSignal @global
;
; @param Z byte address of root widget object (for LPM!)
; @param X pointer to touch event datain SDRAM

Widget_Tree_SendTouchSignal:
  ldi r16, WIDGET_SIGNAL_TOUCH
  clr r17
  ; only check active widgets
  ldi r20, (1<<WIDGET_OPTSLO_INPUT_BIT)
  ldi r21, (1<<WIDGET_OPTSLO_INPUT_BIT)
  rcall Widget_TreeMatchOptsAndSendSignalToActive
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_Tree_SendTimerSignal @global
;
; @param Z byte address of root widget object (for LPM!)

Widget_Tree_SendTimerSignal:
  ldi r16, OBJECT_SIGNAL_TIMER
  clr r17
  ; only check active widgets
  ldi r20, (1<<WIDGET_OPTSLO_TIMER_BIT)
  ldi r21, (1<<WIDGET_OPTSLO_TIMER_BIT)
  rcall Widget_TreeMatchOptsAndSendSignal
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_Tree_SendMsgRecvSignal @global
;
; @param Z byte address of root widget object (for LPM!)
; @param X pointer to received message in SDRAM

Widget_Tree_SendMsgRecvSignal:
  ldi r16, OBJECT_SIGNAL_RECVMSG
  clr r17
  ; only check active widgets
  ldi r20, (1<<WIDGET_OPTSLO_MSGRECV_BIT)
  ldi r21, (1<<WIDGET_OPTSLO_MSGRECV_BIT)
  rcall Widget_TreeMatchOptsAndSendSignal
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_TreeMatchOptsAndSendSignal @global
;
; Send signal to all widgets whose WIDGET_OFFS_OPTS_LO matches the given mask/value.
; Widgets which don't match will not be called, but there children will still be checked
; (as opposed to Widget_TreeMatchingHandleSignal which would also skip the children).
;
; @param  Z byte address of object (for LPM!)
; @param  R16 signal number
; @param  R17 selector
; @param  R20 mask for WIDGET_OFFS_OPTS_LO to match
; @param  R21 value for WIDGET_OFFS_OPTS_LO to match
; @param  xl param1
; @param  xh param2
; @clobbers any, !R16, !R17, !X, !Z

Widget_TreeMatchOptsAndSendSignal:
  push zl
    push zh

Widget_TreeMatchOptsAndSendSignal_loop:
        mov r18, zl
        or r18, zh
        breq Widget_TreeMatchOptsAndSendSignal_done
        adiw zh:zl, WIDGET_OFFS_OPTS_LO
        lpm r18, Z
        sbiw zh:zl, WIDGET_OFFS_OPTS_LO
        eor r18, r21                                   ; XOR with value (1 if different)
        and r18, r20                                   ; AND with mask
        brne Widget_TreeMatchOptsAndSendSignal_next
        push r20
          push r21
            rcall OBJ_HandleSignalSaveInRegs
          pop r21
        pop r20
Widget_TreeMatchOptsAndSendSignal_next:
        push r16
          rcall OBJ_GetBelow                    ; (R16)
        pop r16
        brcc Widget_TreeMatchOptsAndSendSignal_done
        mov zl, r18
        mov zh, r19
      rjmp Widget_TreeMatchOptsAndSendSignal_loop
Widget_TreeMatchOptsAndSendSignal_done:
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_TreeMatchOptsAndSendSignalToActive @global
;
; Send signal to all widgets whose WIDGET_OFFS_OPTS_LO matches the given mask/value.
; Inactive branches are skipped.
;
; @param  Z byte address of object (for LPM!)
; @param  R16 signal number
; @param  R17 selector
; @param  R20 mask for WIDGET_OFFS_OPTS_LO to match
; @param  R21 value for WIDGET_OFFS_OPTS_LO to match
; @param  xl param1
; @param  xh param2
; @clobbers any, !R16, !R17, !X, !Z

Widget_TreeMatchOptsAndSendSignalToActive:
  push zl
    push zh

Widget_TreeMatchOptsAndSendSignalToActive_loop:
        mov r18, zl
        or r18, zh
        breq Widget_TreeMatchOptsAndSendSignalToActive_done

        ; check flags
        rcall Widget_GetSdramPtr        ; (none)
        ldd r18, Y+WIDGET_SD_OFFS_FLAGS
        sbrc r18, WIDGET_FLAGS_ACTIVE_BIT
          rjmp Widget_TreeMatchOptsAndSendSignalToActive_checkOpts
        push r16
          rcall OBJ_GetBelowSkipChildren               ; (R16)
        pop r16
        rjmp Widget_TreeMatchOptsAndSendSignalToActive_next
Widget_TreeMatchOptsAndSendSignalToActive_checkOpts:
        adiw zh:zl, WIDGET_OFFS_OPTS_LO
        lpm r18, Z
        sbiw zh:zl, WIDGET_OFFS_OPTS_LO
        eor r18, r21                                         ; XOR with value (1 if different)
        and r18, r20                                         ; AND with mask
        brne Widget_TreeMatchOptsAndSendSignalToActive_below
        push r20
          push r21
            rcall OBJ_HandleSignalSaveInRegs
          pop r21
        pop r20
Widget_TreeMatchOptsAndSendSignalToActive_below:
        push r16
          rcall OBJ_GetBelow                    ; (R16)
        pop r16
Widget_TreeMatchOptsAndSendSignalToActive_next:
        brcc Widget_TreeMatchOptsAndSendSignalToActive_done
        mov zl, r18
        mov zh, r19
      rjmp Widget_TreeMatchOptsAndSendSignalToActive_loop
Widget_TreeMatchOptsAndSendSignalToActive_done:
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_TreeMatchFlagsAndSendSignalToActive @global
;
; Send signal to all widgets whose WIDGET_SD_OFFS_FLAGS matches the given mask/value.
; Inactive branches are skipped.
;
; @param  Z byte address of object (for LPM!)
; @param  R16 signal number
; @param  R17 selector
; @param  R20 mask for WIDGET_SD_OFFS_FLAGS to match
; @param  R21 value for WIDGET_SD_OFFS_FLAGS to match
; @param  xl param1
; @param  xh param2
; @clobbers any, !R16, !R17, !X, !Z

Widget_TreeMatchFlagsAndSendSignalToActive:
  push zl
    push zh
Widget_TreeMatchFlagsAndSendSignalToActive_loop:
        mov r18, zl
        or r18, zh
        breq Widget_TreeMatchFlagsAndSendSignalToActive_done

        ; check flags
        rcall Widget_GetSdramPtr                             ; (none)
        ldd r18, Y+WIDGET_SD_OFFS_FLAGS
        sbrc r18, WIDGET_FLAGS_ACTIVE_BIT
          rjmp Widget_TreeMatchFlagsAndSendSignalToActive_checkFlags
        push r16
          rcall OBJ_GetBelowSkipChildren                     ; (R16)
        pop r16
        rjmp Widget_TreeMatchFlagsAndSendSignalToActive_next
Widget_TreeMatchFlagsAndSendSignalToActive_checkFlags:
        ldd r18, Y+WIDGET_SD_OFFS_FLAGS
        eor r18, r21                                         ; XOR with value (1 if different)
        and r18, r20                                         ; AND with mask
        brne Widget_TreeMatchFlagsAndSendSignalToActive_below
        push r20
          push r21
            rcall OBJ_HandleSignalSaveInRegs
          pop r21
        pop r20
Widget_TreeMatchFlagsAndSendSignalToActive_below:
        push r16
          rcall OBJ_GetBelow                    ; (R16)
        pop r16
Widget_TreeMatchFlagsAndSendSignalToActive_next:
        brcc Widget_TreeMatchFlagsAndSendSignalToActive_done
        mov zl, r18
        mov zh, r19
      rjmp Widget_TreeMatchFlagsAndSendSignalToActive_loop
Widget_TreeMatchFlagsAndSendSignalToActive_done:
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_AlignPos1D
;
; one-dimensional routine.
;
; @param  R19:R18 size of target
; @param  R13:R12 size of object to align
; @param  
; @param  R17     opts (bit 0: align end, bit 1: align center, bit 2: account for border)
; @return R23:R22 pos
; @clobbers r16, r18, r19

Widget_AlignPos1D:
  clr r22
  clr r23

  sbrc r17, WIDGET_1DALIGN_BORDER_BIT
    rcall Widget_AlignPos1D_subBordersFromSize
  
  sbrc r17, WIDGET_1DALIGN_END_BIT
    rcall Widget_AlignPos1D_alignEnd
  sbrc r17, WIDGET_1DALIGN_CENTER_BIT
    rcall Widget_AlignPos1D_alignCenter

  sbrc r17, WIDGET_1DALIGN_BORDER_BIT
    rcall Widget_AlignPos1D_addBordersToPos
Widget_AlignPos1D_ret:
  ret
Widget_AlignPos1D_alignEnd:
  mov r22, r18
  mov r23, r19
  sub r22, r12
  sbc r23, r13
  brcc Widget_AlignPos1D_ret
  clr r22
  clr r23
  ret
Widget_AlignPos1D_alignCenter:
  rcall Widget_AlignPos1D_alignEnd
  lsr r23
  ror r22
  ret
Widget_AlignPos1D_subBordersFromSize:
  subi r18, 4
  sbci r19, 0
  ret
Widget_AlignPos1D_addBordersToPos:
  ldi r16, 2
  add r22, r16
  adc r23, r16
  sub r23, r16
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_AlignPosXY
;
; @param Z byte address of widget object (for LPM!)
; @param R13:R12 width of object to align
; @return R5:R4  X
; @return R7:R6  Y
; @clobbers r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24, r25

Widget_AlignPosXY:
  adiw zh:zl, WIDGET_OFFS_WIDTH_LO
  lpm r8, Z+    ; WIDTH_LO
  lpm r9, Z+
  lpm r10, Z+   ; HEIGHT_LO
  lpm r11, Z
  sbiw zh:zl, WIDGET_OFFS_WIDTH_LO+3

  adiw zh:zl, WIDGET_OFFS_OPTS_LO
  lpm r24, Z+
  lpm r25, Z
  sbiw zh:zl, WIDGET_OFFS_OPTS_LO+1

  ; align horizontally
  clr r17
  sbrc r24, WIDGET_OPTSLO_BORDER_BIT
    ori r17, (1<<WIDGET_1DALIGN_BORDER_BIT)
  sbrc r25, WIDGET_OPTSHI_HALIGNRIGHT_BIT
    ori r17, (1<<WIDGET_1DALIGN_END_BIT)
  sbrc r25, WIDGET_OPTSHI_HALIGNCENTER_BIT
    ori r17, (1<<WIDGET_1DALIGN_CENTER_BIT)
  mov r18, r8                                   ; widget width
  mov r19, r9
  rcall Widget_AlignPos1D                       ; (r16, r18, r19)
  mov r4, r22
  mov r5, r23

  ; align vertically
  clr r17
  sbrc r24, WIDGET_OPTSLO_BORDER_BIT
    ori r17, (1<<WIDGET_1DALIGN_BORDER_BIT)
  sbrc r25, WIDGET_OPTSHI_VALIGNBOTTOM_BIT
    ori r17, (1<<WIDGET_1DALIGN_END_BIT)
  sbrc r25, WIDGET_OPTSHI_VALIGNCENTER_BIT
    ori r17, (1<<WIDGET_1DALIGN_CENTER_BIT)
  ldi r16, STYLE_WIN_FONT_HEIGHT                ; use font height
  mov r12, r16
  clr r13
  mov r18, r10                                  ; widget height
  mov r19, r11
  rcall Widget_AlignPos1D                       ; (r16, r18, r19)
  mov r6, r22
  mov r7, r23

  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_CalcStringWidthSDRAM
;
; @param  X       pointer to text in SDRAM
; @param  R18     char width in px
; @return r13:r12 width of string
; @clobbers r16

Widget_CalcStringWidthSDRAM:
  clr r12
  clr r13
  push xl
    push xh
Widget_CalcStringWidthSDRAM_loop:
        ld r16, X+
        tst r16
        breq Widget_CalcStringWidthSDRAM_done
        add r12, r18
        adc r13, r18
        sub r13, r18
      rjmp Widget_CalcStringWidthSDRAM_loop
Widget_CalcStringWidthSDRAM_done:
    pop xh
  pop xl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_CalcStringWidthFLASH
;
; @param  Z       pointer to text in FLASH
; @param  R18     char width in px
; @return r13:r12 width of string
; @clobbers r16

Widget_CalcStringWidthFLASH:
  clr r12
  clr r13
  push zl
    push zh
Widget_CalcStringWidthFLASH_loop:
        lpm r16, Z+
        tst r16
        breq Widget_CalcStringWidthFLASH_done
        add r12, r18
        adc r13, r18
        sub r13, r18
      rjmp Widget_CalcStringWidthFLASH_loop
Widget_CalcStringWidthFLASH_done:
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine widgetLoadCoordsForFullWidget
;
; @param Z byte address of widget object (for LPM!)
; @return r5:r4   X (0)
; @return r7:r6   Y (0)
; @return r9:r8   widget width
; @return r11:R10 widget height
; @clobbers none

widgetLoadCoordsForFullWidget:
  clr r4
  clr r5
  clr r6
  clr r7
  adiw zh:zl, WIDGET_OFFS_WIDTH_LO
  lpm r8, Z+    ; WIDTH_LO
  lpm r9, Z+
  lpm r10, Z+   ; HEIGHT_LO
  lpm r11, Z
  sbiw zh:zl, WIDGET_OFFS_WIDTH_LO+3
  ret
; @end



#endif
