diff --git a/avr/devices/c03/main/main.asm b/avr/devices/c03/main/main.asm
index f794433..302dd70 100644
--- a/avr/devices/c03/main/main.asm
+++ b/avr/devices/c03/main/main.asm
@@ -531,18 +531,19 @@ GUI_MODULE_BEGIN:
.include "modules/lcd2/gui/base/hlayout.asm"
.include "modules/lcd2/gui/base/vlayout.asm"
.include "modules/lcd2/gui/base/mclayout.asm"
-.include "modules/lcd2/gui/base/hspinner.asm"
-.include "modules/lcd2/gui/base/mainwindow.asm"
+.include "modules/lcd2/gui/composite/hspinner.asm"
+.include "modules/lcd2/gui/composite/mainwindow.asm"
.include "modules/lcd2/gui/base/keypad.asm"
.include "modules/lcd2/gui/base/keypad_num.asm"
-.include "modules/lcd2/gui/base/d_numinput.asm"
+.include "modules/lcd2/gui/composite/d_numinput.asm"
.include "modules/lcd2/gui/base/cwidget.asm"
-.include "modules/lcd2/gui/base/dialog.asm"
-.include "modules/lcd2/gui/base/cdialog.asm"
+.include "modules/lcd2/gui/composite/dialog.asm"
+.include "modules/lcd2/gui/composite/cdialog.asm"
.include "modules/lcd2/gui/aqhome/sensorwatch.asm"
.include "modules/lcd2/gui/aqhome/lightsettings.asm"
.include "modules/lcd2/gui/aqhome/d_light_conns.asm"
.include "modules/lcd2/gui/aqhome/d_nodevalueid.asm"
+.include "modules/lcd2/gui/composite/textsel.asm"
.include "modules/lcd2/gui/screensavers/simple.asm"
GUI_MODULE_END:
.equ MODULE_SIZE_GUI = GUI_MODULE_END-GUI_MODULE_BEGIN
diff --git a/avr/modules/lcd2/gui/0BUILD b/avr/modules/lcd2/gui/0BUILD
index ea98aaf..433e565 100644
--- a/avr/modules/lcd2/gui/0BUILD
+++ b/avr/modules/lcd2/gui/0BUILD
@@ -5,6 +5,7 @@
aqhome
base
+ composite
screensavers
diff --git a/avr/modules/lcd2/gui/base/0BUILD b/avr/modules/lcd2/gui/base/0BUILD
index 98dbb20..6139d71 100644
--- a/avr/modules/lcd2/gui/base/0BUILD
+++ b/avr/modules/lcd2/gui/base/0BUILD
@@ -4,19 +4,29 @@
button.asm
+ buttongroup.asm
+ checkbox.asm
+ cwidget.asm
guiapp.asm
+ hbuttongroup.asm
hlayout.asm
+ imagebutton.asm
+ imagelabel.asm
imageview.asm
+ keypad.asm
+ keypad_num.asm
label.asm
layout.asm
- mainwindow.asm
mclayout.asm
object.asm
rootwindow.asm
textbutton.asm
+ tile.asm
valuelabel.asm
vlayout.asm
widget.asm
+ wlist.asm
+ wlist_elem.asm
diff --git a/avr/modules/lcd2/gui/base/object.asm b/avr/modules/lcd2/gui/base/object.asm
index 412b435..ae0fdcd 100644
--- a/avr/modules/lcd2/gui/base/object.asm
+++ b/avr/modules/lcd2/gui/base/object.asm
@@ -156,7 +156,7 @@ OBJ_Fini:
; CAVEAT: Y is invalid after return!
;
; @param Y address of object in SDRAM
-; @clobbers none
+; @clobbers any, !Y
OBJ_Free:
tst yl
@@ -1111,6 +1111,42 @@ OBJ_CountDirectChildren_done:
+; ---------------------------------------------------------------------------
+; @routine OBJ_FreeChildren @global
+;
+; @param Y address of object whose children are to be free'd
+; @clobbers any, !Y
+
+OBJ_FreeChildren:
+ push yl
+ push yh
+ clr r16
+ rcall OBJ_GetFirstChild
+ brcc OBJ_FreeChildren_done
+OBJ_FreeChildren_loop:
+ mov yl, r18
+ mov yh, r19
+ rcall OBJ_GetNext
+ brcs OBJ_FreeChildren_loopFree
+ clr r18
+ clr r19
+OBJ_FreeChildren_loopFree:
+ push r18
+ push r19
+ rcall OBJ_Free
+ pop r19
+ pop r18
+ mov r16, r18
+ or r16, r19
+ brne OBJ_FreeChildren_loop
+OBJ_FreeChildren_done:
+ pop yh
+ pop yl
+ ret
+; @end
+
+
+
; ***************************************************************************
; data in FLASH
diff --git a/avr/modules/lcd2/gui/base/widget.asm b/avr/modules/lcd2/gui/base/widget.asm
index a047962..5a4799c 100644
--- a/avr/modules/lcd2/gui/base/widget.asm
+++ b/avr/modules/lcd2/gui/base/widget.asm
@@ -1089,6 +1089,7 @@ Widget_DrawCharAt:
; @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:
@@ -1127,6 +1128,7 @@ Widget_DrawTextRam:
; @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:
diff --git a/avr/modules/lcd2/gui/base/wlist.asm b/avr/modules/lcd2/gui/base/wlist.asm
new file mode 100644
index 0000000..ac61d56
--- /dev/null
+++ b/avr/modules/lcd2/gui/base/wlist.asm
@@ -0,0 +1,200 @@
+; ***************************************************************************
+; 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_WLIST_ASM
+#define AQH_AVR_GUI2_WLIST_ASM
+
+
+; ***************************************************************************
+; defines
+
+.equ WLIST_OFFS_BEGIN = VLAYOUT_SIZE
+.equ WLIST_OFFS_ELEMLIST_LO = VLAYOUT_OFFS_BEGIN+0
+.equ WLIST_OFFS_ELEMLIST_HI = VLAYOUT_OFFS_BEGIN+1
+.equ WLIST_OFFS_ELEMOFFSET = VLAYOUT_OFFS_BEGIN+2
+.equ WLIST_OFFS_ELEMNUM = VLAYOUT_OFFS_BEGIN+3
+.equ WLIST_SIZE = VLAYOUT_OFFS_BEGIN+4
+
+
+; signals
+.equ WLIST_SIGNAL_MKOBJECT = WIDGET_SIGNAL_NEXTFREE+0
+.equ WLIST_SIGNAL_NEXTFREE = WIDGET_SIGNAL_NEXTFREE+1
+
+
+
+; ***************************************************************************
+; code
+
+.cseg
+
+
+
+; ---------------------------------------------------------------------------
+; @routine WList_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
+
+WList_new:
+ ldi r24, LOW(VLAYOUT_SIZE)
+ ldi r25, HIGH(VLAYOUT_SIZE)
+ bigcall Object_Alloc ; (!r16, !r17, !X)
+ brcc WList_new_ret
+ rcall WList_Init ; (r16, r17, X)
+ sec
+WList_new_ret:
+ ret
+; @end
+
+
+
+; ---------------------------------------------------------------------------
+; @routine WList_Init @global
+;
+; @param Y address of widget
+; @param X parent widget (if any)
+; @param r16 value for OBJECT_OFFS_OPTS
+; @param r17 value for WIDGET_OFFS_PACK
+; @clobbers r16, r17, X
+
+WList_Init:
+ ; call base class
+ ldi r20, VLAYOUT_MODE_EXPAND
+ bigcall VLayout_Init ; (r16, r17, X)
+
+ ; set widget-specific data
+
+ ; set default signal map
+ ldi r16, LOW(WList_DefaultSignalmap*2)
+ std Y+OBJECT_OFFS_SIGNALMAP_LO, r16
+ ldi r16, HIGH(WList_DefaultSignalmap*2)
+ std Y+OBJECT_OFFS_SIGNALMAP_HI, r16
+
+ ret
+; @end
+
+
+
+
+wListRebuild:
+ ; release current widgets
+ bigcall OBJ_FreeChildren ; (any, !Y)
+
+ rcall wListGetStartElement
+ brcc wListRebuild_ret
+
+ ; get startY and height
+ bigcall Widget_GetBorderAndSpacing
+ mov r6, r23 ; YPOS (start after border)
+ clr r7
+ ldd r10, Y+WIDGET_OFFS_HEIGHT_LO
+ ldd r11, Y+WIDGET_OFFS_HEIGHT_HI
+ sub r10, r23
+ sbc r11, r23
+ add r11, r23
+
+wListRebuild_loop:
+ ; check height (does element fit?)
+ adiw xh:xl, WLIST_ELEM_OFFS_HEIGHT_LO
+ ld r18, X+
+ ld r19, X
+ sbiw xh:xl, (WLIST_ELEM_OFFS_HEIGHT_LO+1)
+ add r18, r6 ; height+currentY
+ adc r19, r7
+ cp r18, r10
+ cpc r19, r11
+ brcc wListRebuild_ret ; outside window, done
+
+ ; create child widget for item
+ push r6
+ push r7
+ push r10
+ push r11
+ push r22
+ push xl
+ push xh
+ ldi r16, WLIST_SIGNAL_MKOBJECT ; in: list element
+ clr r17
+ bigcall OBJ_HandleSignal ; out: r19:r18=result (if CFLAG set)
+ pop xh
+ pop xl
+ pop r22
+ pop r11
+ pop r10
+ pop r7
+ pop r6
+ brcc wListRebuild_ret
+
+ ; increment Y
+ adiw xh:xl, WLIST_ELEM_OFFS_HEIGHT_LO
+ ld r18, X+
+ ld r19, X
+ sbiw xh:xl, (WLIST_ELEM_OFFS_HEIGHT_LO+1)
+ add r6, r18 ; add item height
+ adc r7, r19
+ add r6, r22 ; add spacing
+ adc r7, r22
+ sub r7, r22
+
+ ; next element
+ ld r16, X+
+ ld r17, X
+ mov xl, r16
+ mov xh, r17
+ or r16, r17
+ brne wListRebuild_loop
+
+wListRebuild_ret:
+ ret
+; @end
+
+
+
+; ---------------------------------------------------------------------------
+; @routine wListGetStartElement @global
+;
+; @param Y address of widget
+; @return CFLAG set if start element found, cleared otherwise
+; @return X pointer to start element
+
+wListGetStartElement:
+ push yl
+ push yh
+ ldd r16, Y+WLIST_OFFS_ELEMOFFSET
+ ldd r17, Y+WLIST_OFFS_ELEMLIST_LO
+ ldd yh, Y+WLIST_OFFS_ELEMLIST_HI
+ mov yl, r17
+ bigcall List_GetItemAt ; X=element at (r16, r17)
+ pop yh
+ pop yl
+ ret
+; @end
+
+
+
+
+; ***************************************************************************
+; data in FLASH
+
+WList_DefaultSignalmap:
+ ; header
+ .dw VLayout_DefaultSignalmap*2 ; next table to use
+ ; entries
+
+ .db 0, 0, 0, 0 ; end of table
+
+
+
+
+#endif
+
diff --git a/avr/modules/lcd2/gui/base/wlist_elem.asm b/avr/modules/lcd2/gui/base/wlist_elem.asm
new file mode 100644
index 0000000..1d51957
--- /dev/null
+++ b/avr/modules/lcd2/gui/base/wlist_elem.asm
@@ -0,0 +1,86 @@
+; ***************************************************************************
+; 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_GUI_WLIST_ELEM_ASM
+#define AQH_AVR_GUI_WLIST_ELEM_ASM
+
+
+
+; ***************************************************************************
+; defines
+
+.equ WLIST_ELEM_OFFS_BEGIN = LIST_SIZE
+.equ WLIST_ELEM_OFFS_IMGRES_LO = WLIST_ELEM_OFFS_BEGIN+0
+.equ WLIST_ELEM_OFFS_IMGRES_HI = WLIST_ELEM_OFFS_BEGIN+1
+.equ WLIST_ELEM_OFFS_TXTRES_LO = WLIST_ELEM_OFFS_BEGIN+2
+.equ WLIST_ELEM_OFFS_TXTRES_HI = WLIST_ELEM_OFFS_BEGIN+3
+.equ WLIST_ELEM_OFFS_USER_LO = WLIST_ELEM_OFFS_BEGIN+4
+.equ WLIST_ELEM_OFFS_USER_HI = WLIST_ELEM_OFFS_BEGIN+5
+.equ WLIST_ELEM_OFFS_HEIGHT_LO = WLIST_ELEM_OFFS_BEGIN+6
+.equ WLIST_ELEM_OFFS_HEIGHT_HI = WLIST_ELEM_OFFS_BEGIN+7
+.equ WLIST_ELEM_SIZE = WLIST_ELEM_OFFS_BEGIN+8
+
+
+
+
+; ***************************************************************************
+; code
+
+.cseg
+
+
+
+; ---------------------------------------------------------------------------
+; @routine WidgetListElem_new @global
+;
+; @return Y address of created object
+
+WidgetListElem_new:
+ ldi r24, LOW(WLIST_ELEM_SIZE)
+ ldi r25, HIGH(WLIST_ELEM_SIZE)
+ bigcall Heap_AllocAndZero
+ brcc WidgetListElem_new_ret
+ mov yl, xl
+ mov yh, xh
+ bigcall List_InitObject ; (r16)
+ sec
+ ret
+WidgetListElem_new_ret:
+ ret
+; @end
+
+
+
+; ---------------------------------------------------------------------------
+; @routine WidgetListElem_free @global
+;
+; @param Y address of object
+; @clobbers r16, r17, r24, r25, X
+
+WidgetListElem_free:
+ tst yl
+ brne WidgetListElem_free_notNull
+ tst yh
+ brne WidgetListElem_free_notNull
+ rjmp WidgetListElem_free_ret
+WidgetListElem_free_notNull:
+ bigcall List_FiniObject ; (r16)
+ mov xl, yl
+ mov xh, yh
+ bigcall Heap_Free ; (r16, r17, r24, r25, X)
+WidgetListElem_free_ret:
+ ret
+; @end
+
+
+
+
+
+
+#endif
diff --git a/avr/modules/lcd2/gui/composite/0BUILD b/avr/modules/lcd2/gui/composite/0BUILD
new file mode 100644
index 0000000..18c9bc1
--- /dev/null
+++ b/avr/modules/lcd2/gui/composite/0BUILD
@@ -0,0 +1,15 @@
+
+
+
+
+
+ cdialog.asm
+ d_numinput.asm
+ dialog.asm
+ hspinner.asm
+ mainwindow.asm
+
+
+
+
+
diff --git a/avr/modules/lcd2/gui/base/cdialog.asm b/avr/modules/lcd2/gui/composite/cdialog.asm
similarity index 100%
rename from avr/modules/lcd2/gui/base/cdialog.asm
rename to avr/modules/lcd2/gui/composite/cdialog.asm
diff --git a/avr/modules/lcd2/gui/base/d_numinput.asm b/avr/modules/lcd2/gui/composite/d_numinput.asm
similarity index 100%
rename from avr/modules/lcd2/gui/base/d_numinput.asm
rename to avr/modules/lcd2/gui/composite/d_numinput.asm
diff --git a/avr/modules/lcd2/gui/base/dialog.asm b/avr/modules/lcd2/gui/composite/dialog.asm
similarity index 100%
rename from avr/modules/lcd2/gui/base/dialog.asm
rename to avr/modules/lcd2/gui/composite/dialog.asm
diff --git a/avr/modules/lcd2/gui/base/hspinner.asm b/avr/modules/lcd2/gui/composite/hspinner.asm
similarity index 100%
rename from avr/modules/lcd2/gui/base/hspinner.asm
rename to avr/modules/lcd2/gui/composite/hspinner.asm
diff --git a/avr/modules/lcd2/gui/base/mainwindow.asm b/avr/modules/lcd2/gui/composite/mainwindow.asm
similarity index 100%
rename from avr/modules/lcd2/gui/base/mainwindow.asm
rename to avr/modules/lcd2/gui/composite/mainwindow.asm
diff --git a/avr/modules/lcd2/gui/composite/textsel.asm b/avr/modules/lcd2/gui/composite/textsel.asm
new file mode 100644
index 0000000..f048ac8
--- /dev/null
+++ b/avr/modules/lcd2/gui/composite/textsel.asm
@@ -0,0 +1,506 @@
+; ***************************************************************************
+; 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_GUI_TEXTSEL_ASM
+#define AQH_AVR_GUI_TEXTSEL_ASM
+
+
+; ***************************************************************************
+; ***************************************************************************
+
+
+
+
+; ***************************************************************************
+; defines
+
+.equ TEXTSEL_OFFS_BEGIN = HLAYOUT_SIZE
+.equ TEXTSEL_OFFS_CURVALUE_LO = TEXTSEL_OFFS_BEGIN+0
+.equ TEXTSEL_OFFS_CURVALUE_HI = TEXTSEL_OFFS_BEGIN+1
+.equ TEXTSEL_OFFS_TEXTLIST_LO = TEXTSEL_OFFS_BEGIN+2
+.equ TEXTSEL_OFFS_TEXTLIST_HI = TEXTSEL_OFFS_BEGIN+3
+.equ TEXTSEL_OFFS_CURRIDX = TEXTSEL_OFFS_BEGIN+4
+.equ TEXTSEL_OFFS_COUNT = TEXTSEL_OFFS_BEGIN+5
+.equ TEXTSEL_SIZE = TEXTSEL_OFFS_BEGIN+6
+
+
+; selectors
+.equ TEXTSEL_SEL_LEFT = 1
+.equ TEXTSEL_SEL_RIGHT = 2
+
+
+; child widgets
+.equ TEXTSEL_CHILDIDX_LEFT = 0
+.equ TEXTSEL_CHILDIDX_VALUE = 1
+.equ TEXTSEL_CHILDIDX_EIGHT = 2
+
+
+; ***************************************************************************
+; code
+
+.cseg
+
+
+
+; ---------------------------------------------------------------------------
+; @routine TextSel_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
+; @param r21:r20 pointer to null-terminated list of text ressources in FLASH (byte address for LPM!)
+; @clobbers any
+
+TextSel_new:
+ ldi r24, LOW(TEXTSEL_SIZE)
+ ldi r25, HIGH(TEXTSEL_SIZE)
+ push r20
+ push r21
+ bigcall Object_Alloc ; (!r16, !r17, !X)
+ pop r21
+ pop r20
+ brcc TextSel_new_ret
+ rcall TextSel_Init ; (any, !Y)
+ sec
+TextSel_new_ret:
+ ret
+; @end
+
+
+
+; ---------------------------------------------------------------------------
+; @routine TextSel_Init @global
+;
+; @param Y address of widget
+; @param X parent widget (if any)
+; @param r16 value for OBJECT_OFFS_OPTS
+; @param r17 value for WIDGET_OFFS_PACK
+; @param r21:r20 pointer to null-terminated list of text ressources in FLASH (byte address for LPM!)
+; @clobbers any, !Y
+
+TextSel_Init:
+ ; call base class
+ push r20
+ push r21
+ ldi r20, HLAYOUT_MODE_EXPAND
+ bigcall HLayout_Init
+ pop r21
+ pop r20
+ brcc TextSel_Init_ret
+
+ ; set values
+ std Y+TEXTSEL_OFFS_TEXTLIST_LO, r20
+ std Y+TEXTSEL_OFFS_TEXTLIST_HI, r21
+
+ ; set default signal map
+ ldi r16, LOW(TextSel_DefaultSignalmap*2)
+ std Y+OBJECT_OFFS_SIGNALMAP_LO, r16
+ ldi r16, HIGH(TextSel_DefaultSignalmap*2)
+ std Y+OBJECT_OFFS_SIGNALMAP_HI, r16
+
+ rcall textSelCreateChildren ; (any, !Y)
+TextSel_Init_ret:
+ ret
+; @end
+
+
+
+; ---------------------------------------------------------------------------
+; @routine textSelCreateChildren
+;
+; @param Y spinner object
+; @clobbers any, !Y
+
+textSelCreateChildren:
+ push yl
+ push yh
+ mov xl, yl ; parent
+ mov xh, yh
+
+ ; create left button
+ call textSelCreateLeftButton
+ brcc textSelCreateChildren_popRet
+ ; create label
+ ldi r16, 0 ; OPTS
+ ldi r17, (WIDGET_PACK_END<