; ***************************************************************************
; 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_BEGIN                 =  OBJECT_SIZE
.equ WIDGET_OFFS_X_LO                  =  WIDGET_OFFS_BEGIN+0
.equ WIDGET_OFFS_X_HI                  =  WIDGET_OFFS_BEGIN+1
.equ WIDGET_OFFS_Y_LO                  =  WIDGET_OFFS_BEGIN+2
.equ WIDGET_OFFS_Y_HI                  =  WIDGET_OFFS_BEGIN+3
.equ WIDGET_OFFS_WIDTH_LO              =  WIDGET_OFFS_BEGIN+4
.equ WIDGET_OFFS_WIDTH_HI              =  WIDGET_OFFS_BEGIN+5
.equ WIDGET_OFFS_HEIGHT_LO             =  WIDGET_OFFS_BEGIN+6
.equ WIDGET_OFFS_HEIGHT_HI             =  WIDGET_OFFS_BEGIN+7
.equ WIDGET_OFFS_STYLE_LO              =  WIDGET_OFFS_BEGIN+8  ; byte address (for LPM!)
.equ WIDGET_OFFS_STYLE_HI              =  WIDGET_OFFS_BEGIN+9
.equ WIDGET_OFFS_PACK                  =  WIDGET_OFFS_BEGIN+10
.equ WIDGET_OFFS_TMP_LO                =  WIDGET_OFFS_BEGIN+11
.equ WIDGET_OFFS_TMP_HI                =  WIDGET_OFFS_BEGIN+12
.equ WIDGET_SIZE                       =  WIDGET_OFFS_BEGIN+13

; widget style object
.equ WIDGET_STYLE_OFFS_FRONTCOL_NORM       =  0
.equ WIDGET_STYLE_OFFS_BACKCOL_NORM        =  2
.equ WIDGET_STYLE_OFFS_BORDERCOL_NORM      =  4
.equ WIDGET_STYLE_OFFS_SHADOWCOL_NORM      =  6
.equ WIDGET_STYLE_OFFS_FRONTCOL_ACTIVATED  =  8
.equ WIDGET_STYLE_OFFS_BACKCOL_ACTIVATED   =  10
.equ WIDGET_STYLE_OFFS_BORDERCOL_ACTIVATED =  12
.equ WIDGET_STYLE_OFFS_SHADOWCOL_ACTIVATED =  14
.equ WIDGET_STYLE_OFFS_OUTERBORDERSIZE     =  16
.equ WIDGET_STYLE_OFFS_SPACING             =  17
.equ WIDGET_STYLE_OFFS_FONT_LO             =  18
.equ WIDGET_STYLE_OFFS_FONT_HI             =  19
.equ WIDGET_STYLE_OFFS_CHARWIDTH           =  20
.equ WIDGET_STYLE_OFFS_CHARHEIGHT          =  21
.equ WIDGET_STYLE_SIZE                     =  22

; widget opts_lo (bits 7 and 6 used by OBJECT_OPTS)
.equ WIDGET_OPTS_INPUT_BIT           =  5 ; TOUCH, KEY
.equ WIDGET_OPTS_BORDER_BIT          =  4

; widget WIDGET_OFFS_PACK
.equ WIDGET_PACK_HSELF0_BIT    = 0
.equ WIDGET_PACK_HSELF1_BIT    = 1
.equ WIDGET_PACK_VSELF0_BIT    = 2
.equ WIDGET_PACK_VSELF1_BIT    = 3
.equ WIDGET_PACK_HCONTENT0_BIT = 4
.equ WIDGET_PACK_HCONTENT1_BIT = 5
.equ WIDGET_PACK_VCONTENT0_BIT = 6
.equ WIDGET_PACK_VCONTENT1_BIT = 7

; values for WIDGET_OFFS_PACK (2 bits)
.equ WIDGET_PACK_BEGIN         =  0
.equ WIDGET_PACK_END           =  1
.equ WIDGET_PACK_CENTER        =  2
.equ WIDGET_PACK_FILLED        =  3

; widget flags (bit 0 used by OBJECT class)
.equ WIDGET_FLAGS_VISIBLE_BIT          =  7 ; widget is visible
.equ WIDGET_FLAGS_TOUCH_BIT            =  6 ; touch in progress
.equ WIDGET_FLAGS_DIRTY_BIT            =  5 ; scheduled for draw
.equ WIDGET_FLAGS_LAYOUT_BIT           =  4 ; scheduled for layout
.equ WIDGET_FLAGS_ACTIVATED_BIT        =  3 ; widget is activated (e.g. button pushed down)
.equ WIDGET_FLAGS_SELECTED_BIT         =  2 ; widget is selected

; SDRAM data for signal WIDGET_SIGNAL_TOUCH
.equ WIDGET_TOUCH_OFFS_X_LO            =  0
.equ WIDGET_TOUCH_OFFS_X_HI            =  1
.equ WIDGET_TOUCH_OFFS_Y_LO            =  2
.equ WIDGET_TOUCH_OFFS_Y_HI            =  3
.equ WIDGET_TOUCH_OFFS_STATE           =  4
.equ WIDGET_TOUCH_SIZE                 =  5

; signals
.equ WIDGET_SIGNAL_SHOW                =  OBJECT_SIGNAL_NEXTFREE+0
.equ WIDGET_SIGNAL_HIDE                =  OBJECT_SIGNAL_NEXTFREE+1
.equ WIDGET_SIGNAL_LAYOUT              =  OBJECT_SIGNAL_NEXTFREE+2
.equ WIDGET_SIGNAL_DRAW                =  OBJECT_SIGNAL_NEXTFREE+3
.equ WIDGET_SIGNAL_TOUCH               =  OBJECT_SIGNAL_NEXTFREE+4
.equ WIDGET_SIGNAL_COMMAND             =  OBJECT_SIGNAL_NEXTFREE+5
.equ WIDGET_SIGNAL_ALTCMD              =  OBJECT_SIGNAL_NEXTFREE+6
.equ WIDGET_SIGNAL_CHANGE              =  OBJECT_SIGNAL_NEXTFREE+7
.equ WIDGET_SIGNAL_SETVALUE            =  OBJECT_SIGNAL_NEXTFREE+8
.equ WIDGET_SIGNAL_GETVALUE            =  OBJECT_SIGNAL_NEXTFREE+9
.equ WIDGET_SIGNAL_KEEPALIVE           =  OBJECT_SIGNAL_NEXTFREE+10
.equ WIDGET_SIGNAL_TOUCH_BEGIN         =  OBJECT_SIGNAL_NEXTFREE+11
.equ WIDGET_SIGNAL_TOUCH_MOVE          =  OBJECT_SIGNAL_NEXTFREE+12
.equ WIDGET_SIGNAL_TOUCH_END           =  OBJECT_SIGNAL_NEXTFREE+13
.equ WIDGET_SIGNAL_DIALOG_END          =  OBJECT_SIGNAL_NEXTFREE+14
.equ WIDGET_SIGNAL_NEXTFREE            =  OBJECT_SIGNAL_NEXTFREE+15

; values for signals WIDGET_SIGNAL_SETVALUE and WIDGET_SIGNAL_GETVALUE
.equ WIDGET_VALUE_DEFAULT_WIDTH        = 1
.equ WIDGET_VALUE_DEFAULT_HEIGHT       = 2
.equ WIDGET_VALUE_STYLE                = 3
.equ WIDGET_VALUE_CURRENTVALUE         = 4
.equ WIDGET_VALUE_MINVALUE             = 5
.equ WIDGET_VALUE_MAXVALUE             = 6
.equ WIDGET_VALUE_NEXTFREE             = 7



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

.cseg



; ---------------------------------------------------------------------------
; @routine Widget_new @global
;
; @return CFLAG set of okay, cleared otherwise
; @return Y address of newly created object
; @param X parent widget
; @param r16 value for OBJECT_OFFS_OPTS
; @param r17 value for WIDGET_OFFS_PACK
; @clobbers any

Widget_new:
  ldi r24, LOW(WIDGET_SIZE)
  ldi r25, HIGH(WIDGET_SIZE)
  bigcall Object_Alloc           ; (!r16, !r17, !X)
  brcc Widget_new_ret
  rcall Widget_Init              ; (r16, r17, X)
  sec
Widget_new_ret:
  ret
; @end


; ---------------------------------------------------------------------------
; @routine Widget_Init @global
;
; @param Y address of object in SDRAM
; @param r16 value for OBJECT_OFFS_OPTS
; @param r17 value for WIDGET_OFFS_PACK
; @param X parent widget (if any)
; @clobbers r16, r17, X

Widget_Init:
  push xl
    push xh
      push r17
        ; call base class
        bigcall OBJ_Init                          ; (r16, r17, X)
      pop r17
    pop xh
  pop xl
  brcc Widget_Init_ret

  std Y+WIDGET_OFFS_PACK, r17
  
  ; set default signal map
  ldi r16, LOW(Widget_DefaultSignalmap*2)
  std Y+OBJECT_OFFS_SIGNALMAP_LO, r16
  ldi r16, HIGH(Widget_DefaultSignalmap*2)
  std Y+OBJECT_OFFS_SIGNALMAP_HI, r16
  
  ; set style
  mov r16, xl
  or r16, xh
  breq Widget_Init_setDefaultStyle
  ; use style from parent
  adiw xh:xl, WIDGET_OFFS_STYLE_LO
  ld r16, X+
  std Y+WIDGET_OFFS_STYLE_LO, r16
  ld r16, X
  std Y+WIDGET_OFFS_STYLE_HI, r16
  sbiw xh:xl, (WIDGET_OFFS_STYLE_LO+1)
  rjmp Widget_Init_setDefaultSize

Widget_Init_setDefaultStyle:
  ldi r16, LOW(Widget_DefaultStyle*2)
  std Y+WIDGET_OFFS_STYLE_LO, r16
  ldi r16, HIGH(Widget_DefaultStyle*2)
  std Y+WIDGET_OFFS_STYLE_HI, r16
  
Widget_Init_setDefaultSize:
  ; initial size: 1x1 px
  ldi r16, 1
  clr r17
  std Y+WIDGET_OFFS_WIDTH_LO, r16
  std Y+WIDGET_OFFS_WIDTH_HI, r17
  std Y+WIDGET_OFFS_HEIGHT_LO, r16
  std Y+WIDGET_OFFS_HEIGHT_HI, r17
  
  ; add to parent (if any)
  mov r16, xl
  or r16, xh
  breq Widget_Init_ret
  bigcall OBJ_AddChild                   ; (r18, r19)
  ; parent needs to layout with the new child
  adiw xh:xl, OBJECT_OFFS_FLAGS
  ld r16, X
  sbr r16, (1<<WIDGET_FLAGS_LAYOUT_BIT)
  sbiw xh:xl, OBJECT_OFFS_FLAGS

  ; preset flags
  ldd r16, Y+OBJECT_OFFS_FLAGS
  sbr r16, (1<<WIDGET_FLAGS_DIRTY_BIT) | (1<<WIDGET_FLAGS_LAYOUT_BIT)
  std Y+OBJECT_OFFS_FLAGS, r16
  sec

Widget_Init_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_Fini @global
;
; @param Y address of object in SDRAM
; @clobbers none

Widget_Fini:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_Resize @global
;
; @param Y address of widget
; @param r9:r8   W
; @param r11:r10 H
; @clobbers r16

Widget_Resize:
  std Y+WIDGET_OFFS_WIDTH_LO, r8
  std Y+WIDGET_OFFS_WIDTH_HI, r9
  std Y+WIDGET_OFFS_HEIGHT_LO, r10
  std Y+WIDGET_OFFS_HEIGHT_HI, r11
  ldd r16, Y+OBJECT_OFFS_FLAGS
  ori r16, (1<<WIDGET_FLAGS_LAYOUT_BIT) | (1<<WIDGET_FLAGS_DIRTY_BIT)
  std Y+OBJECT_OFFS_FLAGS, r16
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_Move @global
;
; @param Y address of widget
; @param r5:r4   X0
; @param r7:r6   Y0
; @clobbers r16

Widget_Move:
  std Y+WIDGET_OFFS_X_LO, r4
  std Y+WIDGET_OFFS_X_HI, r5
  std Y+WIDGET_OFFS_Y_LO, r6
  std Y+WIDGET_OFFS_Y_HI, r7
  ldd r16, Y+OBJECT_OFFS_FLAGS
  ori r16, (1<<WIDGET_FLAGS_LAYOUT_BIT) | (1<<WIDGET_FLAGS_DIRTY_BIT)
  std Y+OBJECT_OFFS_FLAGS, r16
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_SetFullScreen @global
;
; @param Y address of widget
; @clobbers r16

Widget_SetFullScreen:
  ; set pos and size of widget to display size
  clr r16
  std Y+WIDGET_OFFS_X_LO, r16
  std Y+WIDGET_OFFS_X_HI, r16
  std Y+WIDGET_OFFS_Y_LO, r16
  std Y+WIDGET_OFFS_Y_HI, r16
  ldi r16, LOW(DISPLAY_WIDTH)
  std Y+WIDGET_OFFS_WIDTH_LO, r16
  ldi r16, HIGH(DISPLAY_WIDTH)
  std Y+WIDGET_OFFS_WIDTH_HI, r16
  ldi r16, LOW(DISPLAY_HEIGHT)
  std Y+WIDGET_OFFS_HEIGHT_LO, r16
  ldi r16, HIGH(DISPLAY_HEIGHT)
  std Y+WIDGET_OFFS_HEIGHT_HI, r16

  ldd r16, Y+OBJECT_OFFS_FLAGS
  ori r16, (1<<WIDGET_FLAGS_LAYOUT_BIT) | (1<<WIDGET_FLAGS_DIRTY_BIT)
  std Y+OBJECT_OFFS_FLAGS, r16

  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_GetDefaultWidth @global
;
; @param Y address of widget
; @return r19:r18 default width of the widget
; @clobbers any, !Y

Widget_GetDefaultWidth:
  ldi r16, WIDGET_SIGNAL_GETVALUE
  ldi r17, WIDGET_VALUE_DEFAULT_WIDTH
  bigcall OBJ_HandleSignal
  brcs Widget_GetDefaultWidth_ret
  ldi r18, 1
  clr r19
Widget_GetDefaultWidth_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_GetDefaultHeight @global
;
; @param Y address of widget
; @return r19:r18 default height of the widget
; @clobbers any, !Y

Widget_GetDefaultHeight:
  ldi r16, WIDGET_SIGNAL_GETVALUE
  ldi r17, WIDGET_VALUE_DEFAULT_HEIGHT
  bigcall OBJ_HandleSignal
  brcs Widget_GetDefaultHeight_ret
  ldi r18, 1
  clr r19
Widget_GetDefaultHeight_ret:
  ret
; @end


; ---------------------------------------------------------------------------
; @routine Widget_SetValue @global
;
; @param Y address of widget
; @param r17 value id
; @param X value to set
; @return CFLAG set if value set, cleared on error
; @clobbers any, !Y

Widget_SetValue:
  ldi r16, WIDGET_SIGNAL_SETVALUE
  bigjmp OBJ_HandleSignal
; @end



; ---------------------------------------------------------------------------
; @routine Widget_GetValue @global
;
; @param Y address of widget
; @param r17 value id
; @return CFLAG set if value retrieved, cleared otherwise
; @return r19:r18 value
; @clobbers any, !Y

Widget_GetValue:
  ldi r16, WIDGET_SIGNAL_GETVALUE
  bigjmp OBJ_HandleSignal
; @end



; ---------------------------------------------------------------------------
; @routine Widget_SetCurrentValue @global
;
; @param Y address of widget
; @param X value to set
; @return CFLAG set if value set, cleared on error
; @clobbers any, !Y, !Z

Widget_SetCurrentValue:
  push zl
    push zh
      ldi r16, WIDGET_SIGNAL_SETVALUE
      ldi r17, WIDGET_VALUE_CURRENTVALUE
      bigcall OBJ_HandleSignal
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_GetCurrentValue @global
;
; @param Y address of widget
; @param X value to set
; @return CFLAG set if value set, cleared on error
; @clobbers any, !Y, !Z

Widget_GetCurrentValue:
  ldi r17, WIDGET_VALUE_CURRENTVALUE
  rjmp widgetGetValueOrZero
; @end



; ---------------------------------------------------------------------------
; @routine Widget_SetMinValue @global
;
; @param Y address of widget
; @param X value to set
; @return CFLAG set if value set, cleared on error
; @clobbers any, !Y

Widget_SetMinValue:
  ldi r16, WIDGET_SIGNAL_SETVALUE
  ldi r17, WIDGET_VALUE_MINVALUE
  bigjmp OBJ_HandleSignal
; @end



; ---------------------------------------------------------------------------
; @routine Widget_GetMinValue @global
;
; @param Y address of widget
; @return CFLAG set if value set, cleared on error
; @clobbers any, !Y

Widget_GetMinValue:
  ldi r17, WIDGET_VALUE_CURRENTVALUE
  rjmp widgetGetValueOrZero
; @end



; ---------------------------------------------------------------------------
; @routine Widget_SetMaxValue @global
;
; @param Y address of widget
; @param X value to set
; @return CFLAG set if value set, cleared on error
; @clobbers any, !Y

Widget_SetMaxValue:
  ldi r16, WIDGET_SIGNAL_SETVALUE
  ldi r17, WIDGET_VALUE_MAXVALUE
  bigjmp OBJ_HandleSignal
; @end



; ---------------------------------------------------------------------------
; @routine Widget_GetMaxValue @global
;
; @param Y address of widget
; @return CFLAG set if value set, cleared on error
; @clobbers any, !Y

Widget_GetMaxValue:
  ldi r17, WIDGET_VALUE_MAXVALUE
  rjmp widgetGetValueOrZero
; @end



; ---------------------------------------------------------------------------
; @routine Widget_SetStyle @global
;
; @param Y address of widget
; @param X value to set
; @return CFLAG set if value set, cleared on error
; @clobbers any, !Y

Widget_SetStyle:
  ldi r16, WIDGET_SIGNAL_SETVALUE
  ldi r17, WIDGET_VALUE_STYLE
  bigjmp OBJ_HandleSignal
; @end



; ---------------------------------------------------------------------------
; @routine widgetGetValueOrZero @global
;
; @param Y   address of widget
; @param r17 value id to get
; @return CFLAG set if value set, cleared on error
; @clobbers any, !Y, !Z

widgetGetValueOrZero:
  push zl
    push zh
      ldi r16, WIDGET_SIGNAL_GETVALUE
      bigcall OBJ_HandleSignal
    pop zh
  pop zl
  brcs widgetGetValueOrZero_ret
  clr r18
  clr r19
widgetGetValueOrZero_ret:
  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_GetApp @global
;
; @param  Y       address of object
; @return R19:R18 byte address pointer to GuiApp
; @clobbers none

Widget_GetApp:
  push yl
    push yh
      bigcall OBJ_GetRootToY              ; (r18, r19)
      bigcall RootWindow_GetApp           ; (none)
    pop yh
  pop yl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_GetRessource @global
;
; @param r17:r16 ressource id
; @return CFLAG set if ressource found, cleared otherwise
; @return Z pointer to ressource in FLASH (byte address for LPM!)
; @clobbers r16, r17, r18

Widget_GetRessource:
  ldi zl, LOW(RESSOURCE_ADDR*2)
  ldi zh, HIGH(RESSOURCE_ADDR*2)
  bigjmp RES_GetRessource            ; (r16, r17, r18)
; @end



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

Widget_GrabTouchEvents:
  rcall Widget_GetApp                ; r19:r18=guiapp
  push yl
    push yh
      push xl
        push xh
          mov xl, yl
          mov xh, yh
          mov yl, r18
          mov yh, r19
          bigcall GuiApp_GrabTouchEvents ; (r16, r17)
        pop xh
      pop xl
    pop yh
  pop yl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_UngrabTouchEvents @global
;
; Let this widget grab touch events until ungrabbed.
;
; @param  Y       address of object
; @param X byte address of widget grabbing touch events
; @return CFLAG set if grabbed, cleared on error
; @clobbers r16, r17, r18, r19

Widget_UngrabTouchEvents:
  rcall Widget_GetApp                  ; (r18, r19)
  push yl
    push yh
      push xl
        push xh
          mov xl, yl
          mov xh, yh
          mov yl, r18
          mov yh, r19
          bigcall GuiApp_UngrabTouchEvents ; (r16, r17)
        pop xh
      pop xl
    pop yh
  pop yl
  ret
; @end




; ---------------------------------------------------------------------------
; @routine Widget_AddOuterStyleBorders @global
;
; @param  Y       address of object
; @param  r19:r18  value to add the border sizes to
; @return r19:r18  value with added border sizes
; @clobbers r20, r21

Widget_AddOuterStyleBorders:
  push zl
    push zh
      ; add borders
      ldd zl, Y+WIDGET_OFFS_STYLE_LO
      ldd zh, Y+WIDGET_OFFS_STYLE_HI
      adiw zh:zl, WIDGET_STYLE_OFFS_OUTERBORDERSIZE
      lpm r20, Z
      clr r21
      add r18, r20                   ; add border at begin
      adc r19, r21
      add r18, r20                   ; add border at end
      adc r19, r21
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_GetBorderAndSpacing @global
;
; @param Y address of widget
; @return r23 border size
; @return r22 spacing
; @clobbers none

Widget_GetBorderAndSpacing:
  push zl
    push zh
      ldd zl, Y+WIDGET_OFFS_STYLE_LO
      ldd zh, Y+WIDGET_OFFS_STYLE_HI
      mov r22, zl
      or r22, zh
      brne Widget_GetBorderAndSpacing_read
      clr r22
      clr r23
      rjmp Widget_GetBorderAndSpacing_done
Widget_GetBorderAndSpacing_read:
      adiw zh:zl, WIDGET_STYLE_OFFS_OUTERBORDERSIZE
      lpm r23, Z+   ; borders
      lpm r22, Z    ; spacing
Widget_GetBorderAndSpacing_done:
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_OnDraw @global
;
; @param Y address of widget
; @param xl param1
; @param xh param2
; @return CFLAG set if signal handled
; @clobbers any, !Y

Widget_OnDraw:
  ldd r17, Y+OBJECT_OFFS_FLAGS

  ; check whether widget is visible
  sbrs r17, WIDGET_FLAGS_VISIBLE_BIT
    rjmp Widget_OnDraw_ret

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

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

  ldd r17, Y+OBJECT_OFFS_OPTS
  sbrs r17, WIDGET_OPTS_BORDER_BIT
    rjmp Widget_OnDraw_ret
  bigcall Widget_DrawBorder

Widget_OnDraw_ret:
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_OnDrawNop @global
;
; @param Y address of widget
; @param xl param1
; @param xh param2
; @return CFLAG set if signal handled
; @clobbers any, !Y

Widget_OnDrawNop:
  ldd r17, Y+OBJECT_OFFS_FLAGS
  ; only clear dirty bit
  cbr r17, (1<<WIDGET_FLAGS_DIRTY_BIT)
  std Y+OBJECT_OFFS_FLAGS, r17
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_OnLayout @global
;
; @param Y address of widget
; @return CFLAG set if signal handled
; @clobbers any, !Y

Widget_OnLayout:
  ldd r17, Y+OBJECT_OFFS_FLAGS
  ; only clear layout bit
  cbr r17, (1<<WIDGET_FLAGS_LAYOUT_BIT)
  std Y+OBJECT_OFFS_FLAGS, r17
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_OnShow @global
;
; @param Y address of widget
; @return CFLAG set if signal handled
; @clobbers any, !Y

Widget_OnShow:
  ldd r16, Y+OBJECT_OFFS_FLAGS
  sbr r16, (1<<WIDGET_FLAGS_VISIBLE_BIT) | (1<<WIDGET_FLAGS_DIRTY_BIT) | (1<<WIDGET_FLAGS_LAYOUT_BIT)
  std Y+OBJECT_OFFS_FLAGS, r16
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_OnHide @global
;
; @param Y address of widget
; @return CFLAG set if signal handled
; @clobbers any, !Y

Widget_OnHide:
  ldd r16, Y+OBJECT_OFFS_FLAGS
  cbr r16, (1<<WIDGET_FLAGS_VISIBLE_BIT)
  std Y+OBJECT_OFFS_FLAGS, r16
  sec
  ret
; @end


; ---------------------------------------------------------------------------
; @routine Widget_OnGetValue @global
;
; @param Y address of widget
; @param r17 value requested
; @param xl param1
; @param xh param2
; @return CFLAG set if signal handled
; @return r19:r18 value
; @clobbers any, !Y

Widget_OnGetValue:
  cpi r17, WIDGET_VALUE_DEFAULT_WIDTH
  breq Widget_OnGetValue_getDefaultWidth
  cpi r17, WIDGET_VALUE_DEFAULT_HEIGHT
  breq Widget_OnGetValue_getDefaultHeight
  clc
  rjmp Widget_OnGetValue_ret

Widget_OnGetValue_getDefaultWidth:
Widget_OnGetValue_getDefaultHeight:
  ldi r18, 1
  clr r19
  sec
Widget_OnGetValue_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_OnSetValueStyle @global
;
; @param  Y       address of widget
; @param  r17     value requested
; @param  X       style
; @return CFLAG   set if signal handled
; @clobbers any, !Y

Widget_OnSetValueStyle:
  std Y+WIDGET_OFFS_STYLE_LO, xl
  std Y+WIDGET_OFFS_STYLE_HI, xh
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_OnTouch @global
;
; @param Y address of widget
; @param r17 value requested
; @param xl param1
; @param xh param2
; @return CFLAG set if signal handled
; @return r19:r18 value
; @clobbers any, !Y

Widget_OnTouch:
  ldd r16, Y+OBJECT_OFFS_FLAGS
  sbrs r16, WIDGET_FLAGS_VISIBLE_BIT
    rjmp Widget_OnTouch_done

  ; read touch signal data from X
  ld r18, X+                             ; WIDGET_DATA_TOUCH_OFFS_X_LO
  ld r19, X+                             ; WIDGET_DATA_TOUCH_OFFS_X_HI
  ld r20, X+                             ; WIDGET_DATA_TOUCH_OFFS_Y_LO
  ld r21, X+                             ; WIDGET_DATA_TOUCH_OFFS_Z_HI
  ld r22, X                              ; WIDGET_DATA_TOUCH_OFFS_STATE
  sbiw xh:xl, 4

  sbrs r22, DISPLAY_IFLAGS_PRESSED_BIT
    rjmp Widget_OnTouch_up

  ; down (active touch)
  ldd r16, Y+OBJECT_OFFS_FLAGS
  sbrc r16, WIDGET_FLAGS_TOUCH_BIT
    rjmp Widget_OnTouch_move             ; already pressed, jmp
  ; newly pressed
  rcall Widget_LoadDimsForFullWidget     ; r5:r4=X, r7:r6=Y, r9:r8=W, r11:r10=H (none)
  push r18
    push r19
      rcall Widget_MakeAbsPos            ; r5:r4=X, r7:r6=Y (r16, r17, r18, r19)
    pop r19
  pop r18
  rcall Widget_IsPointInRect             ; (R16, R17)
  brcc Widget_OnTouch_done               ; touch not inside this widget, jmp

  ; touch down inside this widget, handle
  bigcall Widget_GrabTouchEvents         ; (r16, r17, r18, r19)
  brcc Widget_OnTouch_done               ; can't grab, abort
  ldd r16, Y+OBJECT_OFFS_FLAGS
  sbr r16, (1<<WIDGET_FLAGS_TOUCH_BIT)
  std Y+OBJECT_OFFS_FLAGS, r16
  ; send TOUCH_BEGIN to this object
  ldi r16, WIDGET_SIGNAL_TOUCH_BEGIN
  clr r17
  bigcall OBJ_HandleSignal               ; (any, !Y)
  rjmp Widget_OnTouch_done
Widget_OnTouch_up:
  ldd r16, Y+OBJECT_OFFS_FLAGS
  sbrs r16, WIDGET_FLAGS_TOUCH_BIT
    rjmp Widget_OnTouch_done
  ; was pressed, not any more
  bigcall Widget_UngrabTouchEvents        ; (r16, r17, r18, r19)
  ldd r16, Y+OBJECT_OFFS_FLAGS
  cbr r16, (1<<WIDGET_FLAGS_TOUCH_BIT)
  std Y+OBJECT_OFFS_FLAGS, r16
  ; send TOUCH_END to this object
  ldi r16, WIDGET_SIGNAL_TOUCH_END
  clr r17
  bigcall OBJ_HandleSignal               ; (any, !Y)
  rjmp Widget_OnTouch_done
Widget_OnTouch_move:
  ; send TOUCH_MOVE to this object
  ldi r16, WIDGET_SIGNAL_TOUCH_MOVE
  clr r17
  bigcall OBJ_HandleSignal               ; (any, !Y)
Widget_OnTouch_done:
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_Clear @global
;
; @param Y address of widget
; @clobbers any, !Y

Widget_Clear:
  rcall Widget_LoadDimsForFullWidget   ; (none)
  rcall Widget_MakeAbsPos             ; (r16, r17, r18, r19)
  rcall Widget_ValidateDims           ; (r16, r17)
  tst r16
  breq Widget_Clear_l1
  ; DEBUG
  ldi r16, LOW(DISPLAY_COLOR_MAGENTA)
  mov r2, r16
  ldi r16, HIGH(DISPLAY_COLOR_MAGENTA)
  mov r3, r16
  bigcall Display_FillRect
  ret
Widget_Clear_l1:

  rcall Widget_SelectColors           ; (R16)
  mov r2, r0                          ; use background
  mov r3, r1
  bigcall Display_FillRect
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_DrawBorder @global
;
; @param Y address of widget
; @clobbers any, !Y

Widget_DrawBorder:
  rcall Widget_LoadDimsForFullWidget   ; (none)
  rcall Widget_MakeAbsPos             ; (R16, R17)
  rcall Widget_SelectBorderColors     ; (R16)
  bigcall Display_DrawRect
Widget_DrawBorder_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_DrawRect @global
;
; @param Y address of widget
; @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_DrawRect:
  rcall Widget_MakeAbsPos             ; (r16, r17, r18, r19)
  bigcall Display_DrawRect
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_FillRect @global
;
; @param Y address of widget
; @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, r18, r19)
;  mov r2, r0                          ; use background
;  mov r3, r1
  bigcall Display_FillRect
  ret
; @end



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

Widget_DrawImage:
  push yl
    push yh
      rcall widgetCalcAbsPosAndBorders    ; (R16, R17)
      bigcall ili9341ImageDraw
    pop yh
  pop yl  
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_DrawCharAt @global
;
; @param  Y       address of widget
; @param  R16     char to write
; @param  R1:R0   background color
; @param  R3:R2   foreground color
; @param  R5:R4   X relative to widget
; @param  R7:R6   Y relative to widget
; @clobbers any, !Y

Widget_DrawCharAt:
  push r16
    rcall widgetCalcAbsPosAndBorders    ; (R16, R17)
  pop r16
  push zl
    push zh
      rcall widgetDrawChar              ; (any, !Y, !R6, !R7, !R8, !R9, !R10, !R11)
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_DrawTextFlash @global
;
; @param  Y       address of widget
; @param  R5:R4   X relative to widget
; @param  R7:R6   Y relative to widget
; @return Z       pointer to next char to write if CFLAG clear
; @return R5:R4   X pos behind last written char
; @clobbers any, !Y

Widget_DrawTextFlash:
  rcall Widget_SelectColors         ; (R16)
  rjmp Widget_DrawColoredTextFlash
; @end



; ---------------------------------------------------------------------------
; @routine Widget_DrawTextRam @global
;
; @param  Y       address of widget
; @param  R5:R4   X relative to widget
; @param  R7:R6   Y relative to widget
; @param  X       pointer to text in SDRAM
; @return CFLAG   set if completely written
; @return X       pointer to next char to write if CFLAG clear
; @clobbers any, !Y

Widget_DrawTextRam:
  rcall Widget_SelectColors         ; (R16)
  rjmp Widget_DrawColoredTextRam
; @end



; ---------------------------------------------------------------------------
; @routine Widget_DrawColoredTextFlash @global
;
; @param  Y       address of widget
; @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  Z       byte address pointer to text in flash (for LPM!)
; @return CFLAG   set if completely written
; @return Z       pointer to next char to write if CFLAG clear
; @return R5:R4   X pos behind last written char
; @clobbers any, !Y

Widget_DrawColoredTextFlash:
  rcall widgetCalcAbsPosAndBorders    ; (R16, R17)

Widget_DrawColoredTextFlash_loop:
    lpm r16, Z
    tst r16
    sec
    breq Widget_DrawColoredTextFlash_loopEnd
    push zl
      push zh
        rcall widgetDrawChar                  ; (any, !Y, !R6, !R7, !R8, !R9, !R10, !R11)
      pop zh
    pop zl
    brcc Widget_DrawColoredTextFlash_loopEnd
    adiw zh:zl, 1
  rjmp Widget_DrawColoredTextFlash_loop
Widget_DrawColoredTextFlash_loopEnd:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_DrawColoredTextRam @global
;
; @param  Y       address of widget
; @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
; @return CFLAG   set if completely written
; @clobbers any, !Y

Widget_DrawColoredTextRam:
  rcall widgetCalcAbsPosAndBorders           ; (R16, R17)

Widget_DrawColoredTextRam_loop:
    ld r16, X
    tst r16
    sec
    breq Widget_DrawColoredTextRam_loopEnd
    push xl
      push xh
        rcall widgetDrawChar                 ; (any, !Y, !R6, !R7, !R8, !R9, !R10, !R11)
      pop xh
    pop xl
    brcc Widget_DrawColoredTextRam_loopEnd
    adiw xh:xl, 1
  rjmp Widget_DrawColoredTextRam_loop
Widget_DrawColoredTextRam_loopEnd:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_MakeAbsPos @global
;
; @param Y address of widget
; @param r5:r4   X
; @param r7:r6   Y
; @clobbers r16, r17, r18, r19

Widget_MakeAbsPos:
  push yl
    push yh
Widget_MakeAbsPos_loop:
        ; adjust X
        ldd r16, Y+WIDGET_OFFS_X_LO
        ldd r17, Y+WIDGET_OFFS_X_HI
        add r4, r16
        adc r5, r17
        ; adjust Y
        ldd r16, Y+WIDGET_OFFS_Y_LO
        ldd r17, Y+WIDGET_OFFS_Y_HI
        add r6, r16
        adc r7, r17
        ; continue with parent
        rcall OBJ_GetParent             ; r19:r18=parent widget (none)
        brcc Widget_MakeAbsPos_loopEnd
        mov yl, r18
        mov yh, r19
      rjmp Widget_MakeAbsPos_loop
Widget_MakeAbsPos_loopEnd:
    pop yh
  pop yl  
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_ValidateDims @global
;
; @param  Y     address of widget
; @param r5:r4   X0
; @param r7:r6   Y0
; @param r9:r8   X1/W
; @param r11:r10 Y1/H
; @return r16    1 if dims modified, 0 if dims where within display range
; @clobbers r16, r17

Widget_ValidateDims:
  push r18
    clr r18
    ldi r16, LOW(DISPLAY_WIDTH)    ; check X
    ldi r17, HIGH(DISPLAY_WIDTH)
    cp r4, r16
    cpc r5, r17
    brcs Widget_ValidateDims_width
    ldi r18, 1
    clr r4
    clr r5
Widget_ValidateDims_width:
    sub r16, r4                    ; calculate remaining width from pos on
    sbc r17, r5
    cp r16, r8
    cpc r17, r9
    brcc Widget_ValidateDims_y
    ldi r18, 1
    mov r8, r16
    mov r9, r17
Widget_ValidateDims_y:
    ldi r16, LOW(DISPLAY_HEIGHT)    ; check Y
    ldi r17, HIGH(DISPLAY_HEIGHT)
    cp r6, r16
    cpc r7, r17
    brcs Widget_ValidateDims_height
    ldi r18, 1
    clr r6
    clr r7
Widget_ValidateDims_height:
    sub r16, r6                    ; calculate remaining width from pos on
    sbc r17, r7
    cp r16, r10
    cpc r17, r11
    brcc Widget_ValidateDims_ret
    ldi r18, 1
    mov r10, r16
    mov r11, r17
Widget_ValidateDims_ret:
    mov r16, r18
  pop r18  
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_CheckDims @global
;
; @param  Y     address of widget
; @clobbers r16, r17, r18, r19

Widget_CheckDims:
  ldi r18, LOW(DISPLAY_WIDTH)    ; check X
  ldi r19, HIGH(DISPLAY_WIDTH)
  ldd r16, Y+WIDGET_OFFS_X_LO
  ldd r17, Y+WIDGET_OFFS_X_HI
  cp r16, r18
  cpc r17, r19
  brcc Widget_CheckDims_invalid
  sub r18, r16
  sbc r19, r17
  ldd r16, Y+WIDGET_OFFS_WIDTH_LO
  ldd r17, Y+WIDGET_OFFS_WIDTH_HI
  cp r18, r16
  cpc r19, r17
  brcs Widget_CheckDims_invalid

  ldi r18, LOW(DISPLAY_HEIGHT)    ; check Y
  ldi r19, HIGH(DISPLAY_HEIGHT)
  ldd r16, Y+WIDGET_OFFS_Y_LO
  ldd r17, Y+WIDGET_OFFS_Y_HI
  cp r16, r18
  cpc r17, r19
  brcc Widget_CheckDims_invalid
  sub r18, r16
  sbc r19, r17
  ldd r16, Y+WIDGET_OFFS_HEIGHT_LO
  ldd r17, Y+WIDGET_OFFS_HEIGHT_HI
  cp r18, r16
  cpc r19, r17
  brcs Widget_CheckDims_invalid
  ; done, all okay so far
  sec
  rjmp Widget_CheckDims_ret
Widget_CheckDims_invalid:
  clc
Widget_CheckDims_ret:
  ret
; @end


; ---------------------------------------------------------------------------
; @routine Widget_SelectColors @global
;
; @param  Y     address of widget
; @return r1:r0 background color
; @return r3:r2 foreground color
; @clobbers R16

Widget_SelectColors:
  ldd r16, Y+OBJECT_OFFS_FLAGS
  sbrc r16, WIDGET_FLAGS_ACTIVATED_BIT
    rjmp Widget_GetActivatedColors
  rjmp Widget_GetNormalColors
; @end



; ---------------------------------------------------------------------------
; @routine Widget_GetNormalColors @global
;
; @param  Y     address of widget
; @return r1:r0 background color
; @return r3:r2 foreground color
; @clobbers none

Widget_GetNormalColors:
  push zl
    push zh
      ldd zl, Y+WIDGET_OFFS_STYLE_LO
      ldd zh, Y+WIDGET_OFFS_STYLE_HI
      adiw zh:zl, WIDGET_STYLE_OFFS_FRONTCOL_NORM
      lpm r2, Z+                 ; read foreground color
      lpm r3, Z+
      lpm r0, Z+                 ; read background color
      lpm r1, Z
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_GetActivatedColors @global
;
; @param  Y     address of widget
; @return r1:r0 background color
; @return r3:r2 foreground color
; @clobbers none

Widget_GetActivatedColors:
  push zl
    push zh
      ldd zl, Y+WIDGET_OFFS_STYLE_LO
      ldd zh, Y+WIDGET_OFFS_STYLE_HI
      adiw zh:zl, WIDGET_STYLE_OFFS_FRONTCOL_ACTIVATED
      lpm r2, Z+                 ; read foreground color
      lpm r3, Z+
      lpm r0, Z+                 ; read background color
      lpm r1, Z
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_SelectBorderColors @global
;
; @param  Y     address of widget
; @return r1:r0 border color
; @return r3:r2 shadow color
; @clobbers R16

Widget_SelectBorderColors:
  push zl
    push zh
      ldd zl, Y+WIDGET_OFFS_STYLE_LO
      ldd zh, Y+WIDGET_OFFS_STYLE_HI
      ldd r16, Y+OBJECT_OFFS_FLAGS
      sbrc r16, WIDGET_FLAGS_ACTIVATED_BIT
        rjmp Widget_SelectBorderColors_activated
      adiw zh:zl, WIDGET_STYLE_OFFS_BORDERCOL_NORM
      rjmp Widget_SelectBorderColors_readColors
Widget_SelectBorderColors_activated:
      adiw zh:zl, WIDGET_STYLE_OFFS_BORDERCOL_ACTIVATED
Widget_SelectBorderColors_readColors:
      lpm r2, Z+                 ; read border color
      lpm r3, Z+
      lpm r0, Z+                 ; read shadow color
      lpm r1, Z
    pop zh
  pop zl
  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 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_CalcFixedStringWidth
;
; @param  Y     address of widget
; @param  r17     number of chars
; @return r13:r12 width of string
; @clobbers R16-R25

Widget_CalcFixedStringWidth:
  push xl
    rcall Widget_GetCharWidth   ; r16=charWidth (none)
    mov r20, r16
    clr r21
    mov r22, r17
    clr r23
    bigcall Utils_Mulu16x16_32  ; (TODO: implement Mulu8x8_16)
    mov r12, r16
    mov r13, r17
  pop xl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_PackSelfX @global
;
; Calc pos according to WIDGET_PACK_HSELF in WIDGET_OFFS_PACK.
;
; @param  Y       address of widget
; @param  R13:R12 width of object to align
; @param  r17 pack mode (see @ref WIDGET_PACK_BEGIN)
; @return R5:R4   X
; @clobbers r17, r18, r19, r20, r21

Widget_PackSelfX:
  ldd r18, Y+WIDGET_OFFS_WIDTH_LO
  ldd r19, Y+WIDGET_OFFS_WIDTH_HI
  andi r17, 3                      ; WIDGET_PACK_HSELF0_BIT = 0, no shift necessary
  rcall widgetPack                 ; r21:r20=new pos (r17, r18, r19)
  mov r4, r20
  mov r5, r21
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_PackSelfY @global
;
; Calc pos according to WIDGET_PACK_VSELF in WIDGET_OFFS_PACK.
;
; @param  Y       address of widget
; @param  R13:R12 height of object to align
; @param  r17 pack mode (see @ref WIDGET_PACK_BEGIN)
; @return R7:R6   Y
; @clobbers r17, r18, r19, r20, r21

Widget_PackSelfY:
  ldd r18, Y+WIDGET_OFFS_HEIGHT_LO
  ldd r19, Y+WIDGET_OFFS_HEIGHT_HI
  lsr r17                          ; WIDGET_PACK_VSELF0_BIT = 2 -> shift 2 times right
  lsr r17
  andi r17, 3
  rcall widgetPack                 ; r21:r20=new pos (r17, r18, r19)
  mov r6, r20
  mov r7, r21
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_PackContentX @global
;
; Calc pos according to WIDGET_PACK_HCONTENT in WIDGET_OFFS_PACK.
;
; @param  Y       address of widget
; @param  R13:R12 width of object to align
; @return R5:R4   X
; @clobbers r17, r18, r19, r20, r21

Widget_PackContentX:
  ldd r18, Y+WIDGET_OFFS_WIDTH_LO
  ldd r19, Y+WIDGET_OFFS_WIDTH_HI
  ldd r17, Y+WIDGET_OFFS_PACK
  swap r17                         ; WIDGET_PACK_HCONTENT0_BIT = 4 -> shift 4 times right
  andi r17, 3
  rcall widgetPack                 ; r21:r20=new pos (r17, r18, r19)
  mov r4, r20
  mov r5, r21
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_PackContentY @global
;
; Calc pos according to WIDGET_PACK_VCONTENT in WIDGET_OFFS_PACK.
;
; @param  Y       address of widget
; @param  R13:R12 height of object to align
; @return R7:R6   Y
; @clobbers r17, r18, r19, r20, r21

Widget_PackContentY:
  ldd r18, Y+WIDGET_OFFS_HEIGHT_LO
  ldd r19, Y+WIDGET_OFFS_HEIGHT_HI
  ldd r17, Y+WIDGET_OFFS_PACK
  swap r17                         ; WIDGET_PACK_VCONTENT0_BIT = 6 -> shift 6 times right
  lsr r17
  lsr r17
  andi r17, 3
  rcall widgetPack                 ; r21:r20=new pos (r17, r18, r19)
  mov r6, r20
  mov r7, r21
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_GetCharWidth @global
;
; @param  Y       address of widget
; @return R16     character width with current font (in pixels)
; @clobbers none

Widget_GetCharWidth:
  push zl
    push zh
      ldd zl, Y+WIDGET_OFFS_STYLE_LO
      ldd zh, Y+WIDGET_OFFS_STYLE_HI
      adiw zh:zl, WIDGET_STYLE_OFFS_CHARWIDTH
      lpm r16, Z
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Widget_GetCharHeight @global
;
; @param  Y       address of widget
; @return R16     character height with current font (in pixels)
; @clobbers none

Widget_GetCharHeight:
  push zl
    push zh
      ldd zl, Y+WIDGET_OFFS_STYLE_LO
      ldd zh, Y+WIDGET_OFFS_STYLE_HI
      adiw zh:zl, WIDGET_STYLE_OFFS_CHARHEIGHT
      lpm r16, Z
    pop zh
  pop zl
  ret
; @end



; ---------------------------------------------------------------------------
; @routine widgetDrawChar
;
; @param Y address of widget
; @param R16 char to write
; @param  R5:R4   absolute X on screen (in display coords)
; @param  R7:R6   absolute Y on screen (in display coords)
; @param  R9:R8   first X pos right of windows (in display coords)
; @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 yl
    push yh
      push r16
        ; get font and charWidth
        ldd zl, Y+WIDGET_OFFS_STYLE_LO
        ldd zh, Y+WIDGET_OFFS_STYLE_HI
        adiw zh:zl, WIDGET_STYLE_OFFS_CHARWIDTH
        lpm r18, Z                                ; char width
        sbiw zh:zl, WIDGET_STYLE_OFFS_CHARWIDTH
        adiw zh:zl, WIDGET_STYLE_OFFS_FONT_LO
        lpm r19, Z+
        lpm zh, Z
        mov zl, r19
        
        ; check whether char fits into widget
        mov r16, r18
        clr r17
        add r16, r4                        ; char width+X
        adc r17, r5
        cp r8, r16                          ; check against window width
        cpc r9, r17
;        sub r16, r8                        ; check against window width
;        sbc r17, r9
      pop r16
      brcs widgetDrawChar_done             ; not fit, jmp
    
      ; draw char
      push r8
        push r9
          push r10
            push r11
              push r18                     ; save char width
                bigcall Display_DrawChar
              pop r18
            pop r11
          pop r10
        pop r9
      pop r8
      ; increment X
      clr r16
      add r4, r18
      adc r5, r16
      sec
widgetDrawChar_done:
    pop yh
  pop yl  
  ret
; @end





; ---------------------------------------------------------------------------
; @routine Widget_LoadDimsForFullWidget
;
; @param Y address of widget
; @return r5:r4   X (0)
; @return r7:r6   Y (0)
; @return r9:r8   widget width
; @return r11:R10 widget height
; @clobbers none

Widget_LoadDimsForFullWidget:
  clr r4
  clr r5
  clr r6
  clr r7
  ldd r8, Y+WIDGET_OFFS_WIDTH_LO
  ldd r9, Y+WIDGET_OFFS_WIDTH_HI
  ldd r10, Y+WIDGET_OFFS_HEIGHT_LO
  ldd r11, Y+WIDGET_OFFS_HEIGHT_HI
  ret
; @end



; ---------------------------------------------------------------------------
; @routine widgetCalcAbsPosAndBorders
;
; @param  Y       address of widget
; @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:
  rcall Widget_MakeAbsPos          ; (R16, R17)
  ldd r8, Y+WIDGET_OFFS_WIDTH_LO
  ldd r9, Y+WIDGET_OFFS_WIDTH_HI
  add r8, r4                       ; convert width to first pos right of widget
  adc r9, r5
  ldd r10, Y+WIDGET_OFFS_HEIGHT_LO
  ldd r11, Y+WIDGET_OFFS_HEIGHT_HI
  add r10, r6
  adc r11, r7                      ; convert height to first pos below widget
  ret
; @end



; ---------------------------------------------------------------------------
; @routine widgetPack
;
; @param  Y       address of widget
; @param  r19:r18 size of target widget (i.e. width or height)
; @param  r17 pack mode (see @ref WIDGET_PACK_BEGIN)
; @param  R13:R12 size of object to pack (i.e. width or height)
; @return R21:R20   pos
; @clobbers r17, r18, r19
; @todo move layoutPack here and use that (shares more code)

widgetPack:
  ; subtract borders
  push zl
    push zh
      ldd zl, Y+WIDGET_OFFS_STYLE_LO
      ldd zh, Y+WIDGET_OFFS_STYLE_HI
      adiw zh:zl, WIDGET_STYLE_OFFS_OUTERBORDERSIZE
      lpm r20, Z
    pop zh
  pop zl
  clr r21
  sub r18, r20                   ; subtract border at begin
  sbc r19, r21
  sub r18, r20                   ; subtract border at end
  sbc r19, r21                   ; r19:r18=host size - (border*2)

  andi r17, 3                    ; only 2 bits pack mode
  cpi r17, WIDGET_PACK_END
  breq widgetPack_end
  cpi r17, WIDGET_PACK_CENTER
  breq widgetPack_center
  rjmp widgetPack_ret            ; begin/filled, align at begin
widgetPack_end:
  sub r18, r12
  sbc r19, r13
  add r20, r18
  adc r21, r19
  rjmp widgetPack_ret
widgetPack_center:
  sub r18, r12
  sbc r19, r13
  lsr r19
  ror r18
  add r20, r18
  adc r21, r19
  rjmp widgetPack_ret
widgetPack_ret:
  ret
; @end





; ***************************************************************************
; data in FLASH

Widget_DefaultSignalmap:
  ; header
  .dw Object_DefaultSignalmap*2  ; next table to use
  ; entries
  .db 0,                  WIDGET_SIGNAL_DRAW,     LOW(Widget_OnDraw),          HIGH(Widget_OnDraw)
  .db 0,                  WIDGET_SIGNAL_LAYOUT,   LOW(Widget_OnLayout),        HIGH(Widget_OnLayout)
  .db 0,                  WIDGET_SIGNAL_GETVALUE, LOW(Widget_OnGetValue),      HIGH(Widget_OnGetValue)  ; handle any value here
  .db WIDGET_VALUE_STYLE, WIDGET_SIGNAL_SETVALUE, LOW(Widget_OnSetValueStyle), HIGH(Widget_OnSetValueStyle)
  .db 0,                  WIDGET_SIGNAL_SHOW,     LOW(Widget_OnShow),          HIGH(Widget_OnShow)
  .db 0,                  WIDGET_SIGNAL_HIDE,     LOW(Widget_OnHide),          HIGH(Widget_OnHide)
  .db 0, 0, 0, 0  ; end of table



Widget_DefaultStyle:
  .dw DISPLAY_COLOR_BLACK      ; frontCol_norm
  .dw DISPLAY_COLOR_LIGHTGREY  ; backCol_norm
  .dw DISPLAY_COLOR_BLACK      ; borderCol_norm
  .dw DISPLAY_COLOR_WHITE      ; shadowCol_norm
  
  .dw DISPLAY_COLOR_WHITE      ; frontCol_activated
  .dw DISPLAY_COLOR_NAVY       ; backCol_activated
  .dw DISPLAY_COLOR_BLACK      ; borderCol_activated
  .dw DISPLAY_COLOR_WHITE      ; shadowCol_activated
  
  .db 0, 1                     ; outerBorderSize, innerBorderSize
  .dw ili9341Font12x16_1*2     ; font
  .db 12, 16                   ; charWidth, charHeight


#endif

