From 21126bb3a847eca89defee72d6831bed44b00a96 Mon Sep 17 00:00:00 2001 From: Thomas Peterson Date: Wed, 4 Feb 2026 18:18:07 +0100 Subject: [PATCH] Backup --- .../index/Fonts.cpp.63891DD6EC3699BC.idx | Bin 8990 -> 9200 bytes ...mCardBubbleWidget.cpp.0672D353A93A1667.idx | Bin 0 -> 6950 bytes ...mCardBubbleWidget.hpp.F5F106CD3B4439EA.idx | Bin 0 -> 1130 bytes ...oomCardTileWidget.cpp.4F5E3794B098F0B3.idx | Bin 0 -> 10574 bytes ...oomCardTileWidget.hpp.D006F134C019997A.idx | Bin 0 -> 1372 bytes .../RoomCardWidget.cpp.FA613078500AD19C.idx | Bin 11514 -> 19160 bytes .../RoomCardWidget.hpp.DE0E3E44BD9BC30A.idx | Bin 2316 -> 3178 bytes ...oomCardWidgetBase.cpp.E82CB8390DB7EE04.idx | Bin 0 -> 7860 bytes ...oomCardWidgetBase.hpp.7649988C886B5357.idx | Bin 0 -> 2362 bytes .../WidgetConfig.cpp.FD56F9F36C29A5DA.idx | Bin 14508 -> 14862 bytes .../WidgetConfig.hpp.CAEFE2EEEB2A6996.idx | Bin 18036 -> 19156 bytes .../WidgetFactory.cpp.1026CAEFCA630F22.idx | Bin 3696 -> 3794 bytes .../WidgetManager.cpp.D8CE609DC911F13E.idx | Bin 61932 -> 62870 bytes main/CMakeLists.txt | 4 +- main/Fonts.cpp | 25 +- main/WidgetConfig.cpp | 14 +- main/WidgetConfig.hpp | 25 +- main/WidgetManager.cpp | 107 +- main/widgets/RoomCardBubbleWidget.cpp | 144 +++ main/widgets/RoomCardBubbleWidget.hpp | 23 + main/widgets/RoomCardTileWidget.cpp | 222 ++++ main/widgets/RoomCardTileWidget.hpp | 30 + main/widgets/RoomCardWidget.cpp | 622 ---------- main/widgets/RoomCardWidget.hpp | 67 -- main/widgets/RoomCardWidgetBase.cpp | 231 ++++ main/widgets/RoomCardWidgetBase.hpp | 53 + main/widgets/WidgetFactory.cpp | 9 +- sdkconfig.defaults | 18 +- web-interface/src/components/CanvasArea.vue | 7 +- web-interface/src/components/SidebarRight.vue | 1006 ++--------------- .../src/components/WidgetElement.vue | 898 +-------------- .../widgets/elements/ButtonElement.vue | 132 +++ .../widgets/elements/ChartElement.vue | 67 ++ .../widgets/elements/ClockElement.vue | 65 ++ .../widgets/elements/IconElement.vue | 75 ++ .../widgets/elements/LabelElement.vue | 112 ++ .../widgets/elements/LedElement.vue | 63 ++ .../widgets/elements/PowerFlowElement.vue | 201 ++++ .../widgets/elements/PowerNodeElement.vue | 132 +++ .../widgets/elements/RoomCardElement.vue | 287 +++++ .../widgets/elements/TabPageElement.vue | 61 + .../widgets/elements/TabViewElement.vue | 126 +++ .../widgets/settings/ButtonSettings.vue | 81 ++ .../widgets/settings/ChartSettings.vue | 108 ++ .../widgets/settings/ClockSettings.vue | 26 + .../widgets/settings/IconSettings.vue | 66 ++ .../widgets/settings/LabelSettings.vue | 113 ++ .../widgets/settings/LedSettings.vue | 55 + .../widgets/settings/PowerFlowSettings.vue | 175 +++ .../widgets/settings/PowerNodeSettings.vue | 258 +++++ .../widgets/settings/RoomCardSettings.vue | 339 ++++++ .../widgets/settings/TabPageSettings.vue | 30 + .../widgets/settings/TabViewSettings.vue | 41 + .../components/widgets/shared/BaseWidget.vue | 67 ++ .../src/components/widgets/shared/styles.js | 11 + .../src/components/widgets/shared/utils.js | 58 + web-interface/src/constants.js | 9 +- web-interface/src/stores/editor.js | 19 +- web-interface/src/utils.js | 15 + 59 files changed, 3815 insertions(+), 2482 deletions(-) create mode 100644 .cache/clangd/index/RoomCardBubbleWidget.cpp.0672D353A93A1667.idx create mode 100644 .cache/clangd/index/RoomCardBubbleWidget.hpp.F5F106CD3B4439EA.idx create mode 100644 .cache/clangd/index/RoomCardTileWidget.cpp.4F5E3794B098F0B3.idx create mode 100644 .cache/clangd/index/RoomCardTileWidget.hpp.D006F134C019997A.idx create mode 100644 .cache/clangd/index/RoomCardWidgetBase.cpp.E82CB8390DB7EE04.idx create mode 100644 .cache/clangd/index/RoomCardWidgetBase.hpp.7649988C886B5357.idx create mode 100644 main/widgets/RoomCardBubbleWidget.cpp create mode 100644 main/widgets/RoomCardBubbleWidget.hpp create mode 100644 main/widgets/RoomCardTileWidget.cpp create mode 100644 main/widgets/RoomCardTileWidget.hpp delete mode 100644 main/widgets/RoomCardWidget.cpp delete mode 100644 main/widgets/RoomCardWidget.hpp create mode 100644 main/widgets/RoomCardWidgetBase.cpp create mode 100644 main/widgets/RoomCardWidgetBase.hpp create mode 100644 web-interface/src/components/widgets/elements/ButtonElement.vue create mode 100644 web-interface/src/components/widgets/elements/ChartElement.vue create mode 100644 web-interface/src/components/widgets/elements/ClockElement.vue create mode 100644 web-interface/src/components/widgets/elements/IconElement.vue create mode 100644 web-interface/src/components/widgets/elements/LabelElement.vue create mode 100644 web-interface/src/components/widgets/elements/LedElement.vue create mode 100644 web-interface/src/components/widgets/elements/PowerFlowElement.vue create mode 100644 web-interface/src/components/widgets/elements/PowerNodeElement.vue create mode 100644 web-interface/src/components/widgets/elements/RoomCardElement.vue create mode 100644 web-interface/src/components/widgets/elements/TabPageElement.vue create mode 100644 web-interface/src/components/widgets/elements/TabViewElement.vue create mode 100644 web-interface/src/components/widgets/settings/ButtonSettings.vue create mode 100644 web-interface/src/components/widgets/settings/ChartSettings.vue create mode 100644 web-interface/src/components/widgets/settings/ClockSettings.vue create mode 100644 web-interface/src/components/widgets/settings/IconSettings.vue create mode 100644 web-interface/src/components/widgets/settings/LabelSettings.vue create mode 100644 web-interface/src/components/widgets/settings/LedSettings.vue create mode 100644 web-interface/src/components/widgets/settings/PowerFlowSettings.vue create mode 100644 web-interface/src/components/widgets/settings/PowerNodeSettings.vue create mode 100644 web-interface/src/components/widgets/settings/RoomCardSettings.vue create mode 100644 web-interface/src/components/widgets/settings/TabPageSettings.vue create mode 100644 web-interface/src/components/widgets/settings/TabViewSettings.vue create mode 100644 web-interface/src/components/widgets/shared/BaseWidget.vue create mode 100644 web-interface/src/components/widgets/shared/styles.js create mode 100644 web-interface/src/components/widgets/shared/utils.js diff --git a/.cache/clangd/index/Fonts.cpp.63891DD6EC3699BC.idx b/.cache/clangd/index/Fonts.cpp.63891DD6EC3699BC.idx index e568f1e6588d9ddff7f79ade63e1d561af11643d..b97eebd07b4bfa39650f2d94ae6b8ef9961b5e2d 100644 GIT binary patch literal 9200 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 literal 8990 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

PC%lNWN|gI!E590NFhj`r+oh1 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/RoomCardBubbleWidget.hpp.F5F106CD3B4439EA.idx b/.cache/clangd/index/RoomCardBubbleWidget.hpp.F5F106CD3B4439EA.idx new file mode 100644 index 0000000000000000000000000000000000000000..b7eb3079906f08ee52f0c3e9a5322ed8267e2b0e GIT binary patch literal 1130 zcmYjQeMl2w7{9ySZNA%ijGc2j6Iu;1N2OAhe^^O5HMP*JV5akH3&ExBCKw8Rs7PrN z22v(Q7EUwEV&#g=De6ChWoG3^7zL?+q$p@cg}s|T<-tAgJp0;7>!zxQo#X0)*~s$xilAyA z#XhtBN#ndy}0K`Z9|o$>o+5(#1tL_ zKS+jTeuUSWGSvF#rfVW~n}ou?h!)dok06i-CK2B z$CeX0N=6Zg=XjMz0KCX2)eRK3gO)fN8tivhuP0f= zMRIXa9|*<;+{A+S&^ocC)I%tkPbKyYI$d=}3MVWS=k$s95qR5&pS=y#j zL#jzyeodJsCR+Z_lh5_u_wUnozW3a7&pq3{_dNNzyScgP^6|N@caPp09Jz+Y$Hyl@ z|K1U~eUsUAKE6Lq`1qo;d$N}Acwr+R|82SVgP1H+5wGpNA?Kn^uC|)RFa2%Bu9b^# zY(6T!+|S)qc!(-!b zSTnBM|63pRbBpYcDBT65xh9=|>A_MDZt z=}GR){BQqU2|1%2Q>(SjGn?J=M#bCh%%FnT^3oj=Gh#Q&?o2cJ;$!GK5N&tpg0Ees z+lcJvlN#Q>HVg7cmel&c))vgt_~slrXc6V7cKgHJ`UKtbZyo>SN8H``<$%YxuLp<6 z_8$GK_cxomL%WV$mME{^`XE1cwrtWHbB}`DZ=+ux53N#oW99a|XZFm*`=!Yjk|MMf z^EYODw?th1@TLCw`v;r_FV-K?I>||FdA-^0?eUWLA@5dwzF(W9BOi6pvfja?;O_d3 z-EM2fTdt+Lja=I^8aW!(VU^JyG+TZk&pkRga^L8mzm3Ld_Uk#ek^sa_?LF}o~dCyF+%4RBE1N&h&SG3wHPCub;WTPBI(r>jmEKh>uTA zT{ELK$z}Ocr=|XjS)nFb0gsXH>LZ@obyCItioR$~+WV?B2}PZeD$U-UOZ_IZZ(8?y zm#nWdu&&${GO&#@J6hp4KfrFkSwQIu-@j9eHf;(yu8{p+BQDYJf-k?7Y@vdG=HVr8 zgtRUh^nWh8d3sm7c%PIiWf7Zib~-sIqUGGB zNQ)*tb5|jaHvZjrtgg22U)g7t@oe=<<15m|n|GG3?^>q1M6kI?gz7%0DA>8-ndI`d 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/RoomCardTileWidget.hpp.D006F134C019997A.idx b/.cache/clangd/index/RoomCardTileWidget.hpp.D006F134C019997A.idx new file mode 100644 index 0000000000000000000000000000000000000000..decdc54ab18593d1593891ab621eb2054cfe909b GIT binary patch literal 1372 zcmYjQX-pGA7@Zw+oTcLKPt%@`%pn`bC2tPb1pdeAJQIsfR2_6_|i+9uz@I*YK zl0XFoBNgKbqVbAEF&Mm1QBctsB0&uhH42CkXNw=&q%)cKX5W6__ukHpHkr%}Lep17 zZ%DP+6KRByH~HJ_)|Itn-%260!EyG$!Jyzo_Bq}l!;tWo%JKl!v{ zuOZETq2#CkkY_7%CY%`+HY?^=#+XIjx4P~e{^{17@!JSp&G~bARQ&qq zU%OU!d}!&}AJ>bvAIz;pwvDOFkC1+P&*nZVPkpDAu!u>E4FS5$RCz3lbxxA78_+z*djq}m_IpR5Ps?l( zdt$`PqHJ(2K%cBCyM|tWiq~QxE7#ejh*8Bq%BJ%><;3-obMo@Ll2G%uQhjU zlCS`@$cg=xm&JFNuvAjEgjGOuBIg572UpIk(>yLMmM{&pnp67&9aj_EDKt&FE@2jE z9jEITe-p5$w%pHB&tfuRo>tMaYeAfAs1(mIj#Z^|cXTv}m?N!eM2+lPKpt5w$+j?} z4DL{z%N5WCR|u@Q0Wi2hV8sQ1!36><(+5}=P@NuWQ+Y(B%b5D@>s)nmV!41$L zx0MLNZHN$omFNHryBpsZnZC~u5y}4?6?m3mwQv=tK&(-c1hCbzCNY^j-pdq!y|mU3 zQBcUT(GyPH`#wjYAVxQZ`4Q1|p1u^gshV-c9=h2UPC=(IQUv10jG{tgb$bN`;j&qi YY+9vQ{z1g?rm8#=AP?m6w|cVu0q&r+d;kCd literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/RoomCardWidget.cpp.FA613078500AD19C.idx b/.cache/clangd/index/RoomCardWidget.cpp.FA613078500AD19C.idx index cb965a0b9036e4469c1b8fac3646ee0b57964b0d..f3d6ca8e017337748e846237e8e6b0561c517755 100644 GIT binary patch literal 19160 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 DF86gB literal 11514 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 Bm<<2` literal 2316 zcmY+E3s6*L6vyxVSa-(IeF_sZ_VuCfnYfdMqrW&kS@2%kYd#xzak5reeIQX(md zA¶@+_67?Y*qq^l?m_&|h41t*;e6>_vfbHtwVFtN_vI#hRN&(8gybH4k1=bYb{ zoERUk;20)0B{456HFueUVHh3!+_^a!dX{0*3B%;=Ih=HHc5|fA6>;uPalTy?32WVG z*?;_4R@b4%U5)c^o=(V$y|MSU=Na4O2fkC{roMM->0m*urat!zX69zU!Q|=Ix z+2M)2K*hFuW7w^U3r|c8G1k`fr@jBjx8>*aBH48lzr1>-r7L{Z-Q>rup4+aBgEO|A zFF)y8y`($)!mYNl7w)XxF+VA@u;|p45BoF=?a@)2ySc_1$CT#D^xBL2Iy-u5S5BJw za=~ElcWHx$1r5IM*5Ay`4=kLnnjRFB$R9|LepL6_>8G#T!q$l8dNK7{C0D@o2K>wQ)8h z2Am7H5tG(Er%)h)5HQ3_L+m(e=j>(-7IRq?9f@4KE`(4lKy zTQrKW;Pw@KEmWyh24MCrKR)}YscR!obWmbFuQzxNx>skG-c8(2_VXkF+!8MtyawIs zNJn<={fv4Q0g9+XsnB~31FXTr(h*ndsy@k^l+nr)J>;7?v*l_gYMaGx~HmXTDM3va5PCTS!je`#Bk*zTb3jr zN*(-OAU@!3XQg&_G*xqI9p)e0Hoc@_V%7tJC?OzTijSa9&N)0i>ZtxV)^|QXAP_%r zmk_ChjHVpN4d*9>|DNmZAX)|l<-X1tm9MO5i(@HOSQK`Y5gAxS1_fmV1!Nfylo1b* zWhhWyJoD_ulV^4ME-cQ%C{ z$W~LTQm817I=o3L^S^JRLZmn=lO5FPN}4jD9$vc28DrQ{U|jmj1+5+ zSi*i}7;!%cBgis>DC15*meE5Q(F0jV4&_Y)=YO1=)L-O7sWw1s#VMkcH}iHZ8>M!O z-GSf313;5yL~$kcBj?!rOG|e`BSP(=;n-YC!^Vb<$L3vG)%)6~8%y@VBq*)QaNHDD z0hW}p#wCY#ANS-fI_80Br``E1b`#x1^EcpLz5)6F9SmWIC1uhv2O$SpCLCpK7|1f& zC~wMZk*DvgcXxZ9bfwfZ@qC3YH8M)N8?#2wMdpAJOY*jmXvP_ToP{zkmdUUW$Y}-@~wb! zPU^~K^PqsrY4y1i-f_pUI6lZMgORnZ6a2#pW7e}sjs2~vz4n%e9v06Q_xXh*m5KA( gSlj~R&ROC1m)oQLr$_KdTepmRue&*YGOK0&0xqPlI{*Lx diff --git a/.cache/clangd/index/RoomCardWidgetBase.cpp.E82CB8390DB7EE04.idx b/.cache/clangd/index/RoomCardWidgetBase.cpp.E82CB8390DB7EE04.idx new file mode 100644 index 0000000000000000000000000000000000000000..428d613ba7d0c4a2a46090e648c60910188d0827 GIT binary patch literal 7860 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 L&huLUcYyRi3f*Xf literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/RoomCardWidgetBase.hpp.7649988C886B5357.idx b/.cache/clangd/index/RoomCardWidgetBase.hpp.7649988C886B5357.idx new file mode 100644 index 0000000000000000000000000000000000000000..7f165ae86f287ef07ee74cd6ee78c4ed5cab506a GIT binary patch literal 2362 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 AJOBUy literal 14508 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 bRW)@DO)Y%`u`;c48Qnoy=n7&!30C+Y*U->M diff --git a/.cache/clangd/index/WidgetConfig.hpp.CAEFE2EEEB2A6996.idx b/.cache/clangd/index/WidgetConfig.hpp.CAEFE2EEEB2A6996.idx index c4b43ebc70ddb88ec9d7e8e7d7757284e094a1f4..d2ca4890918cd08c20fdb3bb1a72bcb27a967dbf 100644 GIT binary patch literal 19156 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 diff --git a/.cache/clangd/index/WidgetFactory.cpp.1026CAEFCA630F22.idx b/.cache/clangd/index/WidgetFactory.cpp.1026CAEFCA630F22.idx index 54dfea722d6f1b36a2ba92980d760997ea7c5569..6bad1a2e337513dd7e1baffaec23b7815e424bb3 100644 GIT binary patch literal 3794 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 diff --git a/.cache/clangd/index/WidgetManager.cpp.D8CE609DC911F13E.idx b/.cache/clangd/index/WidgetManager.cpp.D8CE609DC911F13E.idx index 4d2b1d912976f0d8eb7a1e885c94e529e043c309..6e038f18d5ec5220eb9fd9f1f5155d51a5381016 100644 GIT binary patch delta 15735 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 - + buf[pos++] = iconPositionX & 0xFF; + buf[pos++] = (iconPositionX >> 8) & 0xFF; + buf[pos++] = iconPositionY & 0xFF; + buf[pos++] = (iconPositionY >> 8) & 0xFF; + // Hierarchy buf[pos++] = static_cast(parentId); @@ -178,7 +182,11 @@ void WidgetConfig::deserialize(const uint8_t* buf) { iconPosition = buf[pos++]; iconSize = buf[pos++]; iconGap = static_cast(buf[pos++]); - + iconPositionX = static_cast(buf[pos] | (buf[pos+1] << 8)); + pos += 2; + iconPositionY = static_cast(buf[pos] | (buf[pos+1] << 8)); + pos += 2; + // Hierarchy parentId = static_cast(buf[pos++]); @@ -382,6 +390,8 @@ WidgetConfig WidgetConfig::createButton(uint8_t id, int16_t x, int16_t y, cfg.iconPosition = 0; cfg.iconSize = 1; cfg.iconGap = 8; + cfg.iconPositionX = 0; + cfg.iconPositionY = 0; // Secondary/tertiary address defaults cfg.knxAddress2 = 0; cfg.textSource2 = TextSource::STATIC; diff --git a/main/WidgetConfig.hpp b/main/WidgetConfig.hpp index 4680094..44fbea9 100644 --- a/main/WidgetConfig.hpp +++ b/main/WidgetConfig.hpp @@ -14,6 +14,7 @@ static constexpr size_t CHART_MAX_SERIES = 3; static constexpr size_t MAX_CONDITIONS = 3; static constexpr size_t MAX_FORMAT_LEN = 16; // Shorter format strings for left/right values static constexpr size_t MAX_SUBBUTTONS = 6; // Sub-buttons for RoomCard +static constexpr size_t MAX_TEXTLINES = 5; // Variable text lines for RoomCard enum class WidgetType : uint8_t { LABEL = 0, @@ -180,6 +181,18 @@ struct SubButtonConfig { // Total: 20 bytes per SubButton }; +// Text line configuration for RoomCard (24 bytes) +struct TextLineConfig { + uint32_t iconCodepoint; // 4 bytes - Icon codepoint (0 = no icon) + uint16_t knxAddr; // 2 bytes - KNX address for dynamic value + TextSource textSrc; // 1 byte - Text source (static or KNX DPT) + uint8_t fontSize; // 1 byte - Font size index (0-5) + bool enabled; // 1 byte - Is this text line active? + uint8_t _padding; // 1 byte - Alignment padding + char text[MAX_FORMAT_LEN]; // 16 bytes - Static text or format string + // Total: 26 bytes per TextLine +}; + // Shadow configuration struct ShadowConfig { int8_t offsetX; @@ -225,8 +238,10 @@ struct WidgetConfig { // Icon properties (for Label, Button, Icon widgets) uint32_t iconCodepoint; // Unicode codepoint (0 = no icon) uint8_t iconPosition; // IconPosition: 0=left, 1=right, 2=top, 3=bottom - uint8_t iconSize; // Font size index (0-5), same as fontSize + uint8_t iconSize; // Font size index (0-13 for icons) int8_t iconGap; // Gap between icon and text (px) + int16_t iconPositionX; // Icon Pos X (for decorative icons in Tile) + int16_t iconPositionY; // Icon Pos Y (for decorative icons in Tile) // Hierarchy int8_t parentId; // ID of parent widget (-1 = root/screen) @@ -260,9 +275,13 @@ struct WidgetConfig { uint8_t cardStyle; // 0=Bubble (round), 1=Tile (rectangular) SubButtonConfig subButtons[MAX_SUBBUTTONS]; + // RoomCard text lines (for Tile style) + uint8_t textLineCount; + TextLineConfig textLines[MAX_TEXTLINES]; + // Serialization size (fixed for NVS storage) - // 197 + 1 (subButtonCount) + 1 (subButtonSize) + 1 (subButtonDistance) + 1 (subButtonOpacity) + 1 (cardStyle) + 120 (6 subButtons * 20) = 322 - static constexpr size_t SERIALIZED_SIZE = 322; + // 197 + 4 (iconPositionX/Y) + 1 (subButtonCount) + 1 (subButtonSize) + 1 (subButtonDistance) + 1 (subButtonOpacity) + 1 (cardStyle) + 120 (6 subButtons * 20) = 326 + static constexpr size_t SERIALIZED_SIZE = 326; void serialize(uint8_t* buf) const; void deserialize(const uint8_t* buf); diff --git a/main/WidgetManager.cpp b/main/WidgetManager.cpp index 696cabf..58135ee 100644 --- a/main/WidgetManager.cpp +++ b/main/WidgetManager.cpp @@ -1,6 +1,7 @@ #include "WidgetManager.hpp" #include "widgets/WidgetFactory.hpp" -#include "widgets/RoomCardWidget.hpp" +#include "widgets/RoomCardWidgetBase.hpp" +#include "widgets/RoomCardTileWidget.hpp" #include "HistoryStore.hpp" #include "SdCard.hpp" #include "esp_lv_adapter.h" @@ -1036,6 +1037,22 @@ void WidgetManager::applyCachedValuesToWidgets() { widget->onKnxValue3(value); } } + + // RoomCard text lines (only for Tile style) + if (widget->getType() == WidgetType::ROOMCARD) { + const WidgetConfig& cfg = widget->getConfig(); + if (cfg.cardStyle == 1 && cfg.textLineCount > 0) { + for (uint8_t i = 0; i < cfg.textLineCount && i < MAX_TEXTLINES; ++i) { + const TextLineConfig& tl = cfg.textLines[i]; + if (tl.enabled && tl.knxAddr != 0 && tl.textSrc != TextSource::STATIC && isNumericTextSource(tl.textSrc)) { + float value = 0.0f; + if (getCachedKnxValue(tl.knxAddr, tl.textSrc, &value)) { + static_cast(widget.get())->onTextLineValue(i, value); + } + } + } + } + } } } @@ -1099,6 +1116,19 @@ void WidgetManager::applyKnxValue(uint16_t groupAddr, float value, TextSource so widget->getTextSource3() == source) { widget->onKnxValue3(value); } + + // RoomCard text line updates (only for Tile style) + if (widget->getType() == WidgetType::ROOMCARD) { + const WidgetConfig& cfg = widget->getConfig(); + if (cfg.cardStyle == 1 && cfg.textLineCount > 0) { + for (uint8_t i = 0; i < cfg.textLineCount && i < MAX_TEXTLINES; ++i) { + const TextLineConfig& tl = cfg.textLines[i]; + if (tl.enabled && tl.knxAddr == groupAddr && tl.textSrc == source) { + static_cast(widget.get())->onTextLineValue(i, value); + } + } + } + } } if (HistoryStore::instance().updateLatest(groupAddr, source, value)) { @@ -1119,7 +1149,7 @@ void WidgetManager::applyKnxSwitch(uint16_t groupAddr, bool value) { const WidgetConfig& cfg = widget->getConfig(); for (uint8_t i = 0; i < cfg.subButtonCount && i < MAX_SUBBUTTONS; ++i) { if (cfg.subButtons[i].enabled && cfg.subButtons[i].knxAddrRead == groupAddr) { - static_cast(widget.get())->onSubButtonStatus(i, value); + static_cast(widget.get())->onSubButtonStatus(i, value); } } } @@ -1514,6 +1544,8 @@ void WidgetManager::getConfigJson(char* buf, size_t bufSize) const { cJSON_AddNumberToObject(widget, "iconPosition", w.iconPosition); cJSON_AddNumberToObject(widget, "iconSize", w.iconSize); cJSON_AddNumberToObject(widget, "iconGap", w.iconGap); + cJSON_AddNumberToObject(widget, "iconPositionX", w.iconPositionX); + cJSON_AddNumberToObject(widget, "iconPositionY", w.iconPositionY); cJSON_AddNumberToObject(widget, "parentId", w.parentId); @@ -1637,6 +1669,24 @@ void WidgetManager::getConfigJson(char* buf, size_t bufSize) const { } } + // RoomCard text lines + if (w.type == WidgetType::ROOMCARD && w.textLineCount > 0) { + cJSON* textLines = cJSON_AddArrayToObject(widget, "textLines"); + for (uint8_t ti = 0; ti < w.textLineCount && ti < MAX_TEXTLINES; ++ti) { + const TextLineConfig& tl = w.textLines[ti]; + if (!tl.enabled) continue; + + cJSON* tlJson = cJSON_CreateObject(); + cJSON_AddNumberToObject(tlJson, "icon", tl.iconCodepoint); + cJSON_AddNumberToObject(tlJson, "textSrc", static_cast(tl.textSrc)); + cJSON_AddNumberToObject(tlJson, "knxAddr", tl.knxAddr); + cJSON_AddStringToObject(tlJson, "text", tl.text); + cJSON_AddNumberToObject(tlJson, "fontSize", tl.fontSize); + + cJSON_AddItemToArray(textLines, tlJson); + } + } + cJSON_AddItemToArray(widgets, widget); } @@ -1798,6 +1848,12 @@ bool WidgetManager::updateConfigFromJson(const char* json) { cJSON* iconGap = cJSON_GetObjectItem(widget, "iconGap"); if (cJSON_IsNumber(iconGap)) w.iconGap = iconGap->valueint; + + cJSON* iconPositionX = cJSON_GetObjectItem(widget, "iconPositionX"); + if (cJSON_IsNumber(iconPositionX)) w.iconPositionX = iconPositionX->valueint; + + cJSON* iconPositionY = cJSON_GetObjectItem(widget, "iconPositionY"); + if (cJSON_IsNumber(iconPositionY)) w.iconPositionY = iconPositionY->valueint; cJSON* parentId = cJSON_GetObjectItem(widget, "parentId"); if (cJSON_IsNumber(parentId)) { @@ -1967,8 +2023,10 @@ bool WidgetManager::updateConfigFromJson(const char* json) { cJSON* cardStyle = cJSON_GetObjectItem(widget, "cardStyle"); if (cJSON_IsNumber(cardStyle)) { w.cardStyle = cardStyle->valueint; + ESP_LOGI(TAG, "RoomCard cardStyle loaded: %d", w.cardStyle); } else { w.cardStyle = 0; // Default to Bubble style + ESP_LOGI(TAG, "RoomCard cardStyle not found, using default 0"); } // RoomCard sub-buttons @@ -2028,6 +2086,51 @@ bool WidgetManager::updateConfigFromJson(const char* json) { w.subButtonCount = sbIdx; } + // RoomCard text lines + cJSON* textLines = cJSON_GetObjectItem(widget, "textLines"); + if (cJSON_IsArray(textLines)) { + uint8_t tlIdx = 0; + cJSON* tlItem = nullptr; + cJSON_ArrayForEach(tlItem, textLines) { + if (tlIdx >= MAX_TEXTLINES) break; + + TextLineConfig& tl = w.textLines[tlIdx]; + memset(&tl, 0, sizeof(tl)); + tl.enabled = true; + + cJSON* icon = cJSON_GetObjectItem(tlItem, "icon"); + if (cJSON_IsNumber(icon)) { + tl.iconCodepoint = static_cast(icon->valuedouble); + } + + cJSON* textSrc = cJSON_GetObjectItem(tlItem, "textSrc"); + if (cJSON_IsNumber(textSrc)) { + tl.textSrc = static_cast(textSrc->valueint); + } + + cJSON* knxAddr = cJSON_GetObjectItem(tlItem, "knxAddr"); + if (cJSON_IsNumber(knxAddr)) { + tl.knxAddr = knxAddr->valueint; + } + + cJSON* text = cJSON_GetObjectItem(tlItem, "text"); + if (cJSON_IsString(text)) { + strncpy(tl.text, text->valuestring, MAX_FORMAT_LEN - 1); + tl.text[MAX_FORMAT_LEN - 1] = '\0'; + } + + cJSON* fontSize = cJSON_GetObjectItem(tlItem, "fontSize"); + if (cJSON_IsNumber(fontSize)) { + tl.fontSize = fontSize->valueint; + } else { + tl.fontSize = 1; // Default 18px + } + + tlIdx++; + } + w.textLineCount = tlIdx; + } + screen.widgetCount++; } diff --git a/main/widgets/RoomCardBubbleWidget.cpp b/main/widgets/RoomCardBubbleWidget.cpp new file mode 100644 index 0000000..61a7c14 --- /dev/null +++ b/main/widgets/RoomCardBubbleWidget.cpp @@ -0,0 +1,144 @@ +#include "RoomCardBubbleWidget.hpp" +#include "../Fonts.hpp" +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +RoomCardBubbleWidget::RoomCardBubbleWidget(const WidgetConfig& config) + : RoomCardWidgetBase(config) +{ +} + +void RoomCardBubbleWidget::clearLvglObject() { + RoomCardWidgetBase::clearLvglObject(); + bubble_ = nullptr; + roomIcon_ = nullptr; +} + +lv_obj_t* RoomCardBubbleWidget::create(lv_obj_t* parent) { + parseTextBase(); + + screenParent_ = parent; + + // Create main container + obj_ = lv_obj_create(parent); + lv_obj_remove_style_all(obj_); + lv_obj_set_pos(obj_, config_.x, config_.y); + lv_obj_set_size(obj_, config_.width, config_.height); + lv_obj_clear_flag(obj_, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_add_flag(obj_, LV_OBJ_FLAG_CLICKABLE); + + // Add click handler for navigation + lv_obj_add_event_cb(obj_, cardClickCallback, LV_EVENT_CLICKED, this); + + createBubbleLayout(); + createSubButtonsCommon(); + + return obj_; +} + +void RoomCardBubbleWidget::createBubbleLayout() { + // Calculate bubble size (80% of widget size, circular) + int16_t minSide = config_.width < config_.height ? config_.width : config_.height; + int16_t bubbleSize = (minSide * 80) / 100; + + // Create bubble container (centered) + bubble_ = lv_obj_create(obj_); + lv_obj_remove_style_all(bubble_); + lv_obj_set_size(bubble_, bubbleSize, bubbleSize); + lv_obj_center(bubble_); + lv_obj_clear_flag(bubble_, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_add_flag(bubble_, LV_OBJ_FLAG_CLICKABLE); + + // Circular shape + lv_obj_set_style_radius(bubble_, LV_RADIUS_CIRCLE, 0); + lv_obj_set_style_bg_opa(bubble_, config_.bgOpacity, 0); + lv_obj_set_style_bg_color(bubble_, lv_color_hex(config_.bgColor.toLvColor()), 0); + + // Set up flex layout for bubble content + lv_obj_set_flex_flow(bubble_, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(bubble_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_set_style_pad_all(bubble_, 8, 0); + lv_obj_set_style_pad_gap(bubble_, 2, 0); + + // Room icon + if (config_.iconCodepoint > 0 && Fonts::hasIconFont()) { + roomIcon_ = lv_label_create(bubble_); + lv_obj_clear_flag(roomIcon_, LV_OBJ_FLAG_CLICKABLE); + char iconText[5]; + encodeUtf8(config_.iconCodepoint, iconText); + lv_label_set_text(roomIcon_, iconText); + } + + // Temperature label + tempLabel_ = lv_label_create(bubble_); + lv_obj_clear_flag(tempLabel_, LV_OBJ_FLAG_CLICKABLE); + lv_label_set_text(tempLabel_, tempFormat_[0] != '\0' ? "--" : ""); + + // Room name label + if (roomName_[0] != '\0') { + roomLabel_ = lv_label_create(bubble_); + lv_obj_clear_flag(roomLabel_, LV_OBJ_FLAG_CLICKABLE); + lv_label_set_text(roomLabel_, roomName_); + } + + // Click handler for navigation + lv_obj_add_event_cb(bubble_, cardClickCallback, LV_EVENT_CLICKED, this); +} + +void RoomCardBubbleWidget::calculateSubButtonPosition(uint8_t index, const SubButtonConfig& cfg, int16_t& x, int16_t& y) { + int16_t centerX = config_.width / 2; + int16_t centerY = config_.height / 2; + int16_t subBtnSize = config_.subButtonSize > 0 ? config_.subButtonSize : 40; + int16_t orbitRadius = config_.subButtonDistance > 0 ? config_.subButtonDistance : 80; + + // Calculate angle: position 0=TOP, going clockwise, 8 positions = 45 degrees each + float angle = static_cast(static_cast(cfg.position)) * (M_PI / 4.0f) - (M_PI / 2.0f); + + x = centerX + static_cast(orbitRadius * cosf(angle)) - subBtnSize / 2; + y = centerY + static_cast(orbitRadius * sinf(angle)) - subBtnSize / 2; +} + +void RoomCardBubbleWidget::applyStyle() { + if (!obj_) return; + + lv_color_t textColor = lv_color_hex(config_.textColor.toLvColor()); + + // Apply shadow to bubble if enabled + if (bubble_ && config_.shadow.enabled) { + lv_obj_set_style_shadow_width(bubble_, config_.shadow.blur, 0); + lv_obj_set_style_shadow_ofs_x(bubble_, config_.shadow.offsetX, 0); + lv_obj_set_style_shadow_ofs_y(bubble_, config_.shadow.offsetY, 0); + lv_obj_set_style_shadow_color(bubble_, lv_color_hex(config_.shadow.color.toLvColor()), 0); + lv_obj_set_style_shadow_opa(bubble_, 255, 0); + } + + // Font sizes + const lv_font_t* iconFont = Fonts::iconFont(config_.iconSize); + const lv_font_t* textFont = Fonts::bySizeIndex(config_.fontSize); + const lv_font_t* labelFont = Fonts::bySizeIndex(config_.fontSize > 0 ? config_.fontSize - 1 : 0); + + // Style room icon + if (roomIcon_ && iconFont) { + lv_obj_set_style_text_font(roomIcon_, iconFont, 0); + lv_obj_set_style_text_color(roomIcon_, textColor, 0); + } + + // Style temperature + if (tempLabel_ && textFont) { + lv_obj_set_style_text_font(tempLabel_, textFont, 0); + lv_obj_set_style_text_color(tempLabel_, textColor, 0); + } + + // Style room label + if (roomLabel_ && labelFont) { + lv_obj_set_style_text_font(roomLabel_, labelFont, 0); + lv_obj_set_style_text_color(roomLabel_, textColor, 0); + lv_obj_set_style_text_opa(roomLabel_, 180, 0); // Slightly dimmed + } + + // Style sub-buttons + applySubButtonStyle(); +} diff --git a/main/widgets/RoomCardBubbleWidget.hpp b/main/widgets/RoomCardBubbleWidget.hpp new file mode 100644 index 0000000..42412c1 --- /dev/null +++ b/main/widgets/RoomCardBubbleWidget.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "RoomCardWidgetBase.hpp" + +class RoomCardBubbleWidget : public RoomCardWidgetBase { +public: + explicit RoomCardBubbleWidget(const WidgetConfig& config); + ~RoomCardBubbleWidget() override = default; + + lv_obj_t* create(lv_obj_t* parent) override; + void applyStyle() override; + void clearLvglObject() override; + +protected: + void calculateSubButtonPosition(uint8_t index, const SubButtonConfig& cfg, int16_t& x, int16_t& y) override; + +private: + // Bubble-specific elements + lv_obj_t* bubble_ = nullptr; + lv_obj_t* roomIcon_ = nullptr; + + void createBubbleLayout(); +}; diff --git a/main/widgets/RoomCardTileWidget.cpp b/main/widgets/RoomCardTileWidget.cpp new file mode 100644 index 0000000..2923a90 --- /dev/null +++ b/main/widgets/RoomCardTileWidget.cpp @@ -0,0 +1,222 @@ +#include "RoomCardTileWidget.hpp" +#include "../Fonts.hpp" +#include +#include + +RoomCardTileWidget::RoomCardTileWidget(const WidgetConfig& config) + : RoomCardWidgetBase(config) +{ + for (size_t i = 0; i < MAX_TEXTLINES; ++i) { + textLineLabels_[i] = nullptr; + } +} + +void RoomCardTileWidget::clearLvglObject() { + RoomCardWidgetBase::clearLvglObject(); + decorIcon_ = nullptr; + for (size_t i = 0; i < MAX_TEXTLINES; ++i) { + textLineLabels_[i] = nullptr; + } +} + +lv_obj_t* RoomCardTileWidget::create(lv_obj_t* parent) { + parseTextBase(); // Parses roomName_ from first line of text + + screenParent_ = parent; + + // Create main container + obj_ = lv_obj_create(parent); + lv_obj_remove_style_all(obj_); + lv_obj_set_pos(obj_, config_.x, config_.y); + lv_obj_set_size(obj_, config_.width, config_.height); + lv_obj_clear_flag(obj_, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_add_flag(obj_, LV_OBJ_FLAG_CLICKABLE); + + // Add click handler for navigation + lv_obj_add_event_cb(obj_, cardClickCallback, LV_EVENT_CLICKED, this); + + createTileLayout(); + createTextLines(); + createSubButtonsCommon(); + + return obj_; +} + +void RoomCardTileWidget::createTileLayout() { + // Tile style: rectangular card with rounded corners + int16_t radius = config_.borderRadius; + if (radius <= 0 || radius > 50) { + radius = 16; + } + lv_obj_set_style_radius(obj_, radius, 0); + lv_obj_set_style_bg_opa(obj_, config_.bgOpacity, 0); + lv_obj_set_style_bg_color(obj_, lv_color_hex(config_.bgColor.toLvColor()), 0); + lv_obj_set_style_clip_corner(obj_, true, 0); + + int16_t padding = 16; + + // Room name (top-left) + roomLabel_ = lv_label_create(obj_); + lv_obj_clear_flag(roomLabel_, LV_OBJ_FLAG_CLICKABLE); + lv_label_set_text(roomLabel_, roomName_); + lv_obj_set_pos(roomLabel_, padding, padding); + + // Large decorative icon (bottom-left, partially visible) + if (config_.iconCodepoint > 0 && Fonts::hasIconFont()) { + decorIcon_ = lv_label_create(obj_); + lv_obj_clear_flag(decorIcon_, LV_OBJ_FLAG_CLICKABLE); + char iconText[5]; + encodeUtf8(config_.iconCodepoint, iconText); + lv_label_set_text(decorIcon_, iconText); + + int16_t iconX = (config_.iconPositionX != 0) ? config_.iconPositionX : -20; + int16_t iconY = (config_.iconPositionY != 0) ? config_.iconPositionY : config_.height - 120; + lv_obj_set_pos(decorIcon_, iconX, iconY); + } +} + +void RoomCardTileWidget::createTextLines() { + if (config_.textLineCount == 0) return; + + int16_t padding = 16; + int16_t currentY = 48; // Below room name + + for (uint8_t i = 0; i < config_.textLineCount && i < MAX_TEXTLINES; ++i) { + const TextLineConfig& tl = config_.textLines[i]; + if (!tl.enabled) continue; + + // Get font for this line + uint8_t fontIdx = tl.fontSize; + if (fontIdx > 5) fontIdx = 1; // Default to 18px + const lv_font_t* lineFont = Fonts::bySizeIndex(fontIdx); + const lv_font_t* iconFont = Fonts::iconFont(fontIdx); + + // Create a row container + lv_obj_t* row = lv_obj_create(obj_); + lv_obj_remove_style_all(row); + lv_obj_set_size(row, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + lv_obj_set_pos(row, padding, currentY); + lv_obj_set_flex_flow(row, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(row, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + lv_obj_set_style_pad_gap(row, 6, 0); + lv_obj_clear_flag(row, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_clear_flag(row, LV_OBJ_FLAG_CLICKABLE); + + // Create icon if set + if (tl.iconCodepoint > 0 && Fonts::hasIconFont()) { + lv_obj_t* icon = lv_label_create(row); + lv_obj_clear_flag(icon, LV_OBJ_FLAG_CLICKABLE); + char iconText[5]; + encodeUtf8(tl.iconCodepoint, iconText); + lv_label_set_text(icon, iconText); + if (iconFont) { + lv_obj_set_style_text_font(icon, iconFont, 0); + } + lv_obj_set_style_text_color(icon, lv_color_hex(config_.textColor.toLvColor()), 0); + } + + // Create value label + lv_obj_t* label = lv_label_create(row); + lv_obj_clear_flag(label, LV_OBJ_FLAG_CLICKABLE); + + // Set initial text based on source + if (tl.textSrc == TextSource::STATIC) { + lv_label_set_text(label, tl.text); + } else { + lv_label_set_text(label, "--"); + } + + if (lineFont) { + lv_obj_set_style_text_font(label, lineFont, 0); + } + lv_obj_set_style_text_color(label, lv_color_hex(config_.textColor.toLvColor()), 0); + + textLineLabels_[i] = label; + + // Move Y position for next line + currentY += lv_font_get_line_height(lineFont) + 8; + } +} + +void RoomCardTileWidget::calculateSubButtonPosition(uint8_t index, const SubButtonConfig& cfg, int16_t& x, int16_t& y) { + (void)cfg; // Not used for Tile layout + + // Buttons are stacked vertically on the right side + int16_t subBtnSize = config_.subButtonSize > 0 ? config_.subButtonSize : 40; + int16_t gap = 10; + int16_t padding = 12; + + x = config_.width - subBtnSize - padding; + y = padding + index * (subBtnSize + gap); +} + +void RoomCardTileWidget::updateTextLineValue(uint8_t index, float value) { + if (index >= MAX_TEXTLINES || !textLineLabels_[index]) return; + + const TextLineConfig& tl = config_.textLines[index]; + if (tl.textSrc == TextSource::STATIC) return; + + char buf[32]; + if (tl.text[0] != '\0') { + snprintf(buf, sizeof(buf), tl.text, value); + } else { + // Default formats based on source type + switch (tl.textSrc) { + case TextSource::KNX_DPT_TEMP: + snprintf(buf, sizeof(buf), "%.1f C", value); + break; + case TextSource::KNX_DPT_PERCENT: + snprintf(buf, sizeof(buf), "%.0f %%", value); + break; + case TextSource::KNX_DPT_POWER: + snprintf(buf, sizeof(buf), "%.1f W", value); + break; + case TextSource::KNX_DPT_ENERGY: + snprintf(buf, sizeof(buf), "%.0f kWh", value); + break; + default: + snprintf(buf, sizeof(buf), "%.1f", value); + break; + } + } + lv_label_set_text(textLineLabels_[index], buf); +} + +void RoomCardTileWidget::onTextLineValue(uint8_t index, float value) { + updateTextLineValue(index, value); +} + +void RoomCardTileWidget::applyStyle() { + if (!obj_) return; + + lv_color_t textColor = lv_color_hex(config_.textColor.toLvColor()); + + // Apply border if shadow is enabled (used as accent border) + if (config_.shadow.enabled) { + lv_obj_set_style_border_width(obj_, 3, 0); + lv_obj_set_style_border_color(obj_, lv_color_hex(config_.shadow.color.toLvColor()), 0); + lv_obj_set_style_border_opa(obj_, 255, 0); + } + + // Room name - large font + if (roomLabel_) { + const lv_font_t* nameFont = Fonts::bySizeIndex(config_.fontSize); + lv_obj_set_style_text_font(roomLabel_, nameFont, 0); + lv_obj_set_style_text_color(roomLabel_, textColor, 0); + } + + // Large decorative icon + if (decorIcon_) { + uint8_t decorIconIdx = config_.iconSize; + if (decorIconIdx < 6) decorIconIdx = 8; // Default to 96px for decorative + const lv_font_t* bigIconFont = Fonts::iconFont(decorIconIdx); + if (bigIconFont) { + lv_obj_set_style_text_font(decorIcon_, bigIconFont, 0); + lv_obj_set_style_text_color(decorIcon_, textColor, 0); + lv_obj_set_style_text_opa(decorIcon_, 50, 0); // Very transparent + } + } + + // Style sub-buttons + applySubButtonStyle(); +} diff --git a/main/widgets/RoomCardTileWidget.hpp b/main/widgets/RoomCardTileWidget.hpp new file mode 100644 index 0000000..afa47bd --- /dev/null +++ b/main/widgets/RoomCardTileWidget.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "RoomCardWidgetBase.hpp" + +class RoomCardTileWidget : public RoomCardWidgetBase { +public: + explicit RoomCardTileWidget(const WidgetConfig& config); + ~RoomCardTileWidget() override = default; + + lv_obj_t* create(lv_obj_t* parent) override; + void applyStyle() override; + void clearLvglObject() override; + + // Text line KNX updates + void onTextLineValue(uint8_t index, float value); + +protected: + void calculateSubButtonPosition(uint8_t index, const SubButtonConfig& cfg, int16_t& x, int16_t& y) override; + +private: + // Tile-specific elements + lv_obj_t* decorIcon_ = nullptr; + + // Text line elements (rows with icon + label) + lv_obj_t* textLineLabels_[MAX_TEXTLINES] = {}; + + void createTileLayout(); + void createTextLines(); + void updateTextLineValue(uint8_t index, float value); +}; diff --git a/main/widgets/RoomCardWidget.cpp b/main/widgets/RoomCardWidget.cpp deleted file mode 100644 index b6347cd..0000000 --- a/main/widgets/RoomCardWidget.cpp +++ /dev/null @@ -1,622 +0,0 @@ -#include "RoomCardWidget.hpp" -#include "../Fonts.hpp" -#include "../WidgetManager.hpp" -#include -#include -#include - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -RoomCardWidget::RoomCardWidget(const WidgetConfig& config) - : Widget(config) -{ - roomName_[0] = '\0'; - tempFormat_[0] = '\0'; - subtitle_[0] = '\0'; - humidityFormat_[0] = '\0'; - for (size_t i = 0; i < MAX_SUBBUTTONS; ++i) { - subButtonStates_[i] = false; - } -} - -RoomCardWidget::~RoomCardWidget() { - // Sub-buttons are on screen parent, not on obj_, so delete them explicitly - for (size_t i = 0; i < MAX_SUBBUTTONS; ++i) { - if (subButtonObjs_[i] && lv_obj_is_valid(subButtonObjs_[i])) { - lv_obj_delete(subButtonObjs_[i]); - subButtonObjs_[i] = nullptr; - } - } -} - -void RoomCardWidget::clearLvglObject() { - // Clear sub-button pointers (they'll be deleted when screen is cleaned) - for (size_t i = 0; i < MAX_SUBBUTTONS; ++i) { - subButtonObjs_[i] = nullptr; - subButtonIcons_[i] = nullptr; - } - bubble_ = nullptr; - roomIcon_ = nullptr; - roomLabel_ = nullptr; - tempLabel_ = nullptr; - subtitleLabel_ = nullptr; - humidityLabel_ = nullptr; - decorIcon_ = nullptr; - obj_ = nullptr; -} - -int RoomCardWidget::encodeUtf8(uint32_t codepoint, char* buf) { - if (codepoint < 0x80) { - buf[0] = static_cast(codepoint); - buf[1] = '\0'; - return 1; - } else if (codepoint < 0x800) { - buf[0] = static_cast(0xC0 | (codepoint >> 6)); - buf[1] = static_cast(0x80 | (codepoint & 0x3F)); - buf[2] = '\0'; - return 2; - } else if (codepoint < 0x10000) { - buf[0] = static_cast(0xE0 | (codepoint >> 12)); - buf[1] = static_cast(0x80 | ((codepoint >> 6) & 0x3F)); - buf[2] = static_cast(0x80 | (codepoint & 0x3F)); - buf[3] = '\0'; - return 3; - } else if (codepoint < 0x110000) { - buf[0] = static_cast(0xF0 | (codepoint >> 18)); - buf[1] = static_cast(0x80 | ((codepoint >> 12) & 0x3F)); - buf[2] = static_cast(0x80 | ((codepoint >> 6) & 0x3F)); - buf[3] = static_cast(0x80 | (codepoint & 0x3F)); - buf[4] = '\0'; - return 4; - } - buf[0] = '\0'; - return 0; -} - -void RoomCardWidget::parseText() { - roomName_[0] = '\0'; - tempFormat_[0] = '\0'; - subtitle_[0] = '\0'; - humidityFormat_[0] = '\0'; - - // Format for Bubble: "RoomName\nTempFormat" - // Format for Tile: "RoomName\nSubtitle" (temp/humidity via text2/text3) - const char* text = config_.text; - if (text && text[0] != '\0') { - const char* newline = strchr(text, '\n'); - if (newline) { - size_t nameLen = static_cast(newline - text); - if (nameLen >= MAX_TEXT_LEN) nameLen = MAX_TEXT_LEN - 1; - memcpy(roomName_, text, nameLen); - roomName_[nameLen] = '\0'; - - if (config_.cardStyle == 0) { - // Bubble: second line is temp format - strncpy(tempFormat_, newline + 1, MAX_TEXT_LEN - 1); - tempFormat_[MAX_TEXT_LEN - 1] = '\0'; - } else { - // Tile: second line is subtitle - strncpy(subtitle_, newline + 1, MAX_TEXT_LEN - 1); - subtitle_[MAX_TEXT_LEN - 1] = '\0'; - } - } else { - strncpy(roomName_, text, MAX_TEXT_LEN - 1); - roomName_[MAX_TEXT_LEN - 1] = '\0'; - } - } - - // For Tile style, use text2 for temp format and text3 for humidity - if (config_.cardStyle == 1) { - if (config_.text2[0] != '\0') { - strncpy(tempFormat_, config_.text2, MAX_FORMAT_LEN - 1); - tempFormat_[MAX_FORMAT_LEN - 1] = '\0'; - } - if (config_.text3[0] != '\0') { - strncpy(humidityFormat_, config_.text3, MAX_FORMAT_LEN - 1); - humidityFormat_[MAX_FORMAT_LEN - 1] = '\0'; - } - } -} - -void RoomCardWidget::calculateSubButtonPosition(SubButtonPosition pos, int16_t& x, int16_t& y) { - int16_t centerX = config_.width / 2; - int16_t centerY = config_.height / 2; - - // Sub-button size (configurable, default 40) - int16_t subBtnSize = config_.subButtonSize > 0 ? config_.subButtonSize : 40; - - // Sub-buttons orbit radius in pixels (default 80) - int16_t orbitRadius = config_.subButtonDistance > 0 ? config_.subButtonDistance : 80; - - // Calculate angle: 0=TOP, going clockwise, 8 positions = 45 degrees each - float angle = static_cast(static_cast(pos)) * (M_PI / 4.0f) - (M_PI / 2.0f); - - // Calculate position (center of sub-button) - x = centerX + static_cast(orbitRadius * cosf(angle)) - subBtnSize / 2; - y = centerY + static_cast(orbitRadius * sinf(angle)) - subBtnSize / 2; -} - -void RoomCardWidget::calculateSubButtonPositionTile(uint8_t index, int16_t& x, int16_t& y) { - // For Tile layout: buttons are stacked vertically on the right side - int16_t subBtnSize = config_.subButtonSize > 0 ? config_.subButtonSize : 40; - int16_t gap = 10; // Gap between buttons - int16_t padding = 12; // Padding from edge - - x = config_.width - subBtnSize - padding; - y = padding + index * (subBtnSize + gap); -} - -lv_obj_t* RoomCardWidget::create(lv_obj_t* parent) { - parseText(); - - // Store parent for sub-buttons (they're created on screen level to avoid clipping) - screenParent_ = parent; - - // Create main container - obj_ = lv_obj_create(parent); - lv_obj_remove_style_all(obj_); - lv_obj_set_pos(obj_, config_.x, config_.y); - lv_obj_set_size(obj_, config_.width, config_.height); - lv_obj_clear_flag(obj_, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_add_flag(obj_, LV_OBJ_FLAG_CLICKABLE); - - // Add click handler for navigation - lv_obj_add_event_cb(obj_, bubbleClickCallback, LV_EVENT_CLICKED, this); - - // Create layout based on card style - if (config_.cardStyle == 1) { - createTileLayout(); - createSubButtonsTile(); - } else { - createBubbleLayout(); - createSubButtons(); - } - - return obj_; -} - -void RoomCardWidget::createBubbleLayout() { - // Calculate bubble size (80% of widget size, circular) - int16_t minSide = config_.width < config_.height ? config_.width : config_.height; - int16_t bubbleSize = (minSide * 80) / 100; - - // Create bubble container (centered) - bubble_ = lv_obj_create(obj_); - lv_obj_remove_style_all(bubble_); - lv_obj_set_size(bubble_, bubbleSize, bubbleSize); - lv_obj_center(bubble_); - lv_obj_clear_flag(bubble_, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_add_flag(bubble_, LV_OBJ_FLAG_CLICKABLE); - - // Circular shape - lv_obj_set_style_radius(bubble_, LV_RADIUS_CIRCLE, 0); - lv_obj_set_style_bg_opa(bubble_, config_.bgOpacity, 0); - lv_obj_set_style_bg_color(bubble_, lv_color_hex(config_.bgColor.toLvColor()), 0); - - // Set up flex layout for bubble content - lv_obj_set_flex_flow(bubble_, LV_FLEX_FLOW_COLUMN); - lv_obj_set_flex_align(bubble_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - lv_obj_set_style_pad_all(bubble_, 8, 0); - lv_obj_set_style_pad_gap(bubble_, 2, 0); - - // Room icon - if (config_.iconCodepoint > 0 && Fonts::hasIconFont()) { - roomIcon_ = lv_label_create(bubble_); - lv_obj_clear_flag(roomIcon_, LV_OBJ_FLAG_CLICKABLE); - char iconText[5]; - encodeUtf8(config_.iconCodepoint, iconText); - lv_label_set_text(roomIcon_, iconText); - } - - // Temperature label - tempLabel_ = lv_label_create(bubble_); - lv_obj_clear_flag(tempLabel_, LV_OBJ_FLAG_CLICKABLE); - lv_label_set_text(tempLabel_, tempFormat_[0] != '\0' ? "--" : ""); - - // Room name label - if (roomName_[0] != '\0') { - roomLabel_ = lv_label_create(bubble_); - lv_obj_clear_flag(roomLabel_, LV_OBJ_FLAG_CLICKABLE); - lv_label_set_text(roomLabel_, roomName_); - } - - // Click handler for navigation - lv_obj_add_event_cb(bubble_, bubbleClickCallback, LV_EVENT_CLICKED, this); -} - -void RoomCardWidget::createTileLayout() { - // Tile style: rectangular card with rounded corners - lv_obj_set_style_radius(obj_, config_.borderRadius > 0 ? config_.borderRadius : 16, 0); - lv_obj_set_style_bg_opa(obj_, config_.bgOpacity, 0); - lv_obj_set_style_bg_color(obj_, lv_color_hex(config_.bgColor.toLvColor()), 0); - lv_obj_set_style_clip_corner(obj_, true, 0); - - int16_t padding = 16; - - // Room name (top-left, large) - roomLabel_ = lv_label_create(obj_); - lv_obj_clear_flag(roomLabel_, LV_OBJ_FLAG_CLICKABLE); - lv_label_set_text(roomLabel_, roomName_); - lv_obj_set_pos(roomLabel_, padding, padding); - - // Subtitle (below room name, smaller) - if (subtitle_[0] != '\0') { - subtitleLabel_ = lv_label_create(obj_); - lv_obj_clear_flag(subtitleLabel_, LV_OBJ_FLAG_CLICKABLE); - lv_label_set_text(subtitleLabel_, subtitle_); - lv_obj_set_pos(subtitleLabel_, padding, padding + 28); - } - - // Temperature row (with thermometer icon) - if (tempFormat_[0] != '\0') { - // Container for temp icon + value - lv_obj_t* tempRow = lv_obj_create(obj_); - lv_obj_remove_style_all(tempRow); - lv_obj_set_size(tempRow, LV_SIZE_CONTENT, LV_SIZE_CONTENT); - lv_obj_set_pos(tempRow, padding, padding + 56); - lv_obj_set_flex_flow(tempRow, LV_FLEX_FLOW_ROW); - lv_obj_set_flex_align(tempRow, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - lv_obj_set_style_pad_gap(tempRow, 4, 0); - lv_obj_clear_flag(tempRow, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_clear_flag(tempRow, LV_OBJ_FLAG_CLICKABLE); - - // Thermometer icon (U+E1FF or similar) - if (Fonts::hasIconFont()) { - lv_obj_t* tempIcon = lv_label_create(tempRow); - lv_obj_clear_flag(tempIcon, LV_OBJ_FLAG_CLICKABLE); - char iconText[5]; - encodeUtf8(0xf076, iconText); // thermometer icon - lv_label_set_text(tempIcon, iconText); - } - - tempLabel_ = lv_label_create(tempRow); - lv_obj_clear_flag(tempLabel_, LV_OBJ_FLAG_CLICKABLE); - lv_label_set_text(tempLabel_, "--"); - } - - // Humidity row (with water drop icon) - if (humidityFormat_[0] != '\0') { - lv_obj_t* humRow = lv_obj_create(obj_); - lv_obj_remove_style_all(humRow); - lv_obj_set_size(humRow, LV_SIZE_CONTENT, LV_SIZE_CONTENT); - lv_obj_set_pos(humRow, padding + 80, padding + 56); // Next to temp - lv_obj_set_flex_flow(humRow, LV_FLEX_FLOW_ROW); - lv_obj_set_flex_align(humRow, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - lv_obj_set_style_pad_gap(humRow, 4, 0); - lv_obj_clear_flag(humRow, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_clear_flag(humRow, LV_OBJ_FLAG_CLICKABLE); - - // Water drop icon - if (Fonts::hasIconFont()) { - lv_obj_t* humIcon = lv_label_create(humRow); - lv_obj_clear_flag(humIcon, LV_OBJ_FLAG_CLICKABLE); - char iconText[5]; - encodeUtf8(0xe798, iconText); // humidity/water icon - lv_label_set_text(humIcon, iconText); - } - - humidityLabel_ = lv_label_create(humRow); - lv_obj_clear_flag(humidityLabel_, LV_OBJ_FLAG_CLICKABLE); - lv_label_set_text(humidityLabel_, "--"); - } - - // Large decorative icon (bottom-left, partially visible) - if (config_.iconCodepoint > 0 && Fonts::hasIconFont()) { - decorIcon_ = lv_label_create(obj_); - lv_obj_clear_flag(decorIcon_, LV_OBJ_FLAG_CLICKABLE); - char iconText[5]; - encodeUtf8(config_.iconCodepoint, iconText); - lv_label_set_text(decorIcon_, iconText); - // Position at bottom-left, slightly outside - lv_obj_set_pos(decorIcon_, -20, config_.height - 100); - } -} - -void RoomCardWidget::createSubButtons() { - // Sub-button size (configurable, default 40) - int16_t subBtnSize = config_.subButtonSize > 0 ? config_.subButtonSize : 40; - - for (uint8_t i = 0; i < config_.subButtonCount && i < MAX_SUBBUTTONS; ++i) { - const SubButtonConfig& cfg = config_.subButtons[i]; - if (!cfg.enabled) continue; - - // Create sub-button on screen parent (not on widget container) to avoid clipping - lv_obj_t* btn = lv_obj_create(screenParent_); - lv_obj_remove_style_all(btn); - lv_obj_set_size(btn, subBtnSize, subBtnSize); - lv_obj_clear_flag(btn, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_add_flag(btn, LV_OBJ_FLAG_CLICKABLE); - - // Circular shape - lv_obj_set_style_radius(btn, LV_RADIUS_CIRCLE, 0); - lv_obj_set_style_bg_opa(btn, config_.subButtonOpacity, 0); - - // Position using circle geometry (relative to widget center) - int16_t relX, relY; - calculateSubButtonPosition(cfg.position, relX, relY); - // Convert to absolute screen position - int16_t absX = config_.x + relX; - int16_t absY = config_.y + relY; - lv_obj_set_pos(btn, absX, absY); - - // Create icon - if (cfg.iconCodepoint > 0 && Fonts::hasIconFont()) { - lv_obj_t* icon = lv_label_create(btn); - lv_obj_clear_flag(icon, LV_OBJ_FLAG_CLICKABLE); - char iconText[5]; - encodeUtf8(cfg.iconCodepoint, iconText); - lv_label_set_text(icon, iconText); - lv_obj_center(icon); - subButtonIcons_[i] = icon; - } - - // Store index in user_data for click handler - lv_obj_set_user_data(btn, reinterpret_cast(static_cast(i))); - lv_obj_add_event_cb(btn, subButtonClickCallback, LV_EVENT_CLICKED, this); - - subButtonObjs_[i] = btn; - subButtonStates_[i] = false; - - // Apply initial color (OFF state) - updateSubButtonColor(i); - } -} - -void RoomCardWidget::createSubButtonsTile() { - // For Tile layout: buttons are positioned on the right side, vertically stacked - int16_t subBtnSize = config_.subButtonSize > 0 ? config_.subButtonSize : 40; - - for (uint8_t i = 0; i < config_.subButtonCount && i < MAX_SUBBUTTONS; ++i) { - const SubButtonConfig& cfg = config_.subButtons[i]; - if (!cfg.enabled) continue; - - // Create sub-button on screen parent - lv_obj_t* btn = lv_obj_create(screenParent_); - lv_obj_remove_style_all(btn); - lv_obj_set_size(btn, subBtnSize, subBtnSize); - lv_obj_clear_flag(btn, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_add_flag(btn, LV_OBJ_FLAG_CLICKABLE); - - // Circular shape - lv_obj_set_style_radius(btn, LV_RADIUS_CIRCLE, 0); - lv_obj_set_style_bg_opa(btn, config_.subButtonOpacity, 0); - - // Position on right side - int16_t relX, relY; - calculateSubButtonPositionTile(i, relX, relY); - int16_t absX = config_.x + relX; - int16_t absY = config_.y + relY; - lv_obj_set_pos(btn, absX, absY); - - // Create icon - if (cfg.iconCodepoint > 0 && Fonts::hasIconFont()) { - lv_obj_t* icon = lv_label_create(btn); - lv_obj_clear_flag(icon, LV_OBJ_FLAG_CLICKABLE); - char iconText[5]; - encodeUtf8(cfg.iconCodepoint, iconText); - lv_label_set_text(icon, iconText); - lv_obj_center(icon); - subButtonIcons_[i] = icon; - } - - // Store index in user_data for click handler - lv_obj_set_user_data(btn, reinterpret_cast(static_cast(i))); - lv_obj_add_event_cb(btn, subButtonClickCallback, LV_EVENT_CLICKED, this); - - subButtonObjs_[i] = btn; - subButtonStates_[i] = false; - - // Apply initial color (OFF state) - updateSubButtonColor(i); - } -} - -void RoomCardWidget::applyStyle() { - if (!obj_) return; - - lv_color_t textColor = lv_color_hex(config_.textColor.toLvColor()); - - if (config_.cardStyle == 1) { - // Tile style - // Apply border if shadow is enabled (used as accent border) - if (config_.shadow.enabled) { - lv_obj_set_style_border_width(obj_, 3, 0); - lv_obj_set_style_border_color(obj_, lv_color_hex(config_.shadow.color.toLvColor()), 0); - lv_obj_set_style_border_opa(obj_, 255, 0); - } - - // Room name - large font - if (roomLabel_) { - const lv_font_t* nameFont = Fonts::bySizeIndex(config_.fontSize); - lv_obj_set_style_text_font(roomLabel_, nameFont, 0); - lv_obj_set_style_text_color(roomLabel_, textColor, 0); - } - - // Subtitle - smaller, dimmed - if (subtitleLabel_) { - const lv_font_t* subFont = Fonts::bySizeIndex(config_.fontSize > 1 ? config_.fontSize - 2 : 0); - lv_obj_set_style_text_font(subtitleLabel_, subFont, 0); - lv_obj_set_style_text_color(subtitleLabel_, textColor, 0); - lv_obj_set_style_text_opa(subtitleLabel_, 150, 0); - } - - // Temp and humidity - medium font - const lv_font_t* valueFont = Fonts::bySizeIndex(config_.fontSize > 0 ? config_.fontSize - 1 : 0); - const lv_font_t* iconFont = Fonts::iconFont(1); // Small icons - - if (tempLabel_) { - lv_obj_set_style_text_font(tempLabel_, valueFont, 0); - lv_obj_set_style_text_color(tempLabel_, textColor, 0); - // Style the icon in the same row - lv_obj_t* parent = lv_obj_get_parent(tempLabel_); - if (parent && lv_obj_get_child_count(parent) > 0) { - lv_obj_t* iconLabel = lv_obj_get_child(parent, 0); - if (iconLabel && iconFont) { - lv_obj_set_style_text_font(iconLabel, iconFont, 0); - lv_obj_set_style_text_color(iconLabel, textColor, 0); - } - } - } - - if (humidityLabel_) { - lv_obj_set_style_text_font(humidityLabel_, valueFont, 0); - lv_obj_set_style_text_color(humidityLabel_, textColor, 0); - lv_obj_t* parent = lv_obj_get_parent(humidityLabel_); - if (parent && lv_obj_get_child_count(parent) > 0) { - lv_obj_t* iconLabel = lv_obj_get_child(parent, 0); - if (iconLabel && iconFont) { - lv_obj_set_style_text_font(iconLabel, iconFont, 0); - lv_obj_set_style_text_color(iconLabel, textColor, 0); - } - } - } - - // Large decorative icon - if (decorIcon_) { - const lv_font_t* bigIconFont = Fonts::iconFont(5); // Largest icon - if (bigIconFont) { - lv_obj_set_style_text_font(decorIcon_, bigIconFont, 0); - lv_obj_set_style_text_color(decorIcon_, textColor, 0); - lv_obj_set_style_text_opa(decorIcon_, 60, 0); // Very transparent - } - } - } else { - // Bubble style - // Apply shadow to bubble if enabled - if (bubble_ && config_.shadow.enabled) { - lv_obj_set_style_shadow_width(bubble_, config_.shadow.blur, 0); - lv_obj_set_style_shadow_ofs_x(bubble_, config_.shadow.offsetX, 0); - lv_obj_set_style_shadow_ofs_y(bubble_, config_.shadow.offsetY, 0); - lv_obj_set_style_shadow_color(bubble_, lv_color_hex(config_.shadow.color.toLvColor()), 0); - lv_obj_set_style_shadow_opa(bubble_, 255, 0); - } - - // Font sizes - const lv_font_t* iconFont = Fonts::iconFont(config_.iconSize); - const lv_font_t* textFont = Fonts::bySizeIndex(config_.fontSize); - const lv_font_t* labelFont = Fonts::bySizeIndex(config_.fontSize > 0 ? config_.fontSize - 1 : 0); - - // Style room icon - if (roomIcon_ && iconFont) { - lv_obj_set_style_text_font(roomIcon_, iconFont, 0); - lv_obj_set_style_text_color(roomIcon_, textColor, 0); - } - - // Style temperature - if (tempLabel_ && textFont) { - lv_obj_set_style_text_font(tempLabel_, textFont, 0); - lv_obj_set_style_text_color(tempLabel_, textColor, 0); - } - - // Style room label - if (roomLabel_ && labelFont) { - lv_obj_set_style_text_font(roomLabel_, labelFont, 0); - lv_obj_set_style_text_color(roomLabel_, textColor, 0); - lv_obj_set_style_text_opa(roomLabel_, 180, 0); // Slightly dimmed - } - } - - // Style sub-buttons - adjust icon size based on button size - uint8_t subBtnFontIdx = 1; // Default small - if (config_.subButtonSize > 55) { - subBtnFontIdx = 3; - } else if (config_.subButtonSize > 40) { - subBtnFontIdx = 2; - } - const lv_font_t* subBtnIconFont = Fonts::iconFont(subBtnFontIdx); - for (uint8_t i = 0; i < config_.subButtonCount && i < MAX_SUBBUTTONS; ++i) { - if (subButtonIcons_[i] && subBtnIconFont) { - lv_obj_set_style_text_font(subButtonIcons_[i], subBtnIconFont, 0); - lv_obj_set_style_text_color(subButtonIcons_[i], lv_color_white(), 0); - } - } -} - -void RoomCardWidget::updateSubButtonColor(uint8_t index) { - if (index >= MAX_SUBBUTTONS || !subButtonObjs_[index]) return; - - const SubButtonConfig& cfg = config_.subButtons[index]; - const Color& color = subButtonStates_[index] ? cfg.colorOn : cfg.colorOff; - - lv_obj_set_style_bg_color(subButtonObjs_[index], lv_color_hex(color.toLvColor()), 0); -} - -void RoomCardWidget::updateTemperature(float value) { - if (!tempLabel_ || tempFormat_[0] == '\0') return; - - char buf[32]; - snprintf(buf, sizeof(buf), tempFormat_, value); - lv_label_set_text(tempLabel_, buf); -} - -void RoomCardWidget::updateHumidity(float value) { - if (!humidityLabel_ || humidityFormat_[0] == '\0') return; - - char buf[32]; - snprintf(buf, sizeof(buf), humidityFormat_, value); - lv_label_set_text(humidityLabel_, buf); -} - -void RoomCardWidget::onKnxValue(float value) { - cachedPrimaryValue_ = value; - hasCachedPrimary_ = true; - updateTemperature(value); -} - -void RoomCardWidget::onKnxSwitch(bool value) { - // Not used directly - sub-button status is handled via onSubButtonStatus - (void)value; -} - -void RoomCardWidget::onSubButtonStatus(uint8_t index, bool value) { - if (index >= MAX_SUBBUTTONS) return; - - subButtonStates_[index] = value; - updateSubButtonColor(index); -} - -void RoomCardWidget::sendSubButtonToggle(uint8_t index) { - if (index >= config_.subButtonCount || index >= MAX_SUBBUTTONS) return; - - const SubButtonConfig& cfg = config_.subButtons[index]; - if (cfg.action == SubButtonAction::TOGGLE_KNX && cfg.knxAddrWrite > 0) { - bool newState = !subButtonStates_[index]; - // Send KNX toggle via WidgetManager - WidgetManager::instance().sendKnxSwitch(cfg.knxAddrWrite, newState); - // Optimistically update local state - subButtonStates_[index] = newState; - updateSubButtonColor(index); - } -} - -void RoomCardWidget::bubbleClickCallback(lv_event_t* e) { - RoomCardWidget* widget = static_cast(lv_event_get_user_data(e)); - if (!widget) return; - - // Handle navigation based on action - if (widget->config_.action == ButtonAction::JUMP) { - WidgetManager::instance().navigateToScreen(widget->config_.targetScreen); - } else if (widget->config_.action == ButtonAction::BACK) { - WidgetManager::instance().navigateBack(); - } -} - -void RoomCardWidget::subButtonClickCallback(lv_event_t* e) { - RoomCardWidget* widget = static_cast(lv_event_get_user_data(e)); - lv_obj_t* target = static_cast(lv_event_get_target(e)); - if (!widget || !target) return; - - uint8_t index = static_cast(reinterpret_cast(lv_obj_get_user_data(target))); - - if (index < widget->config_.subButtonCount && index < MAX_SUBBUTTONS) { - const SubButtonConfig& cfg = widget->config_.subButtons[index]; - - if (cfg.action == SubButtonAction::TOGGLE_KNX) { - widget->sendSubButtonToggle(index); - } else if (cfg.action == SubButtonAction::NAVIGATE) { - WidgetManager::instance().navigateToScreen(cfg.targetScreen); - } - } -} diff --git a/main/widgets/RoomCardWidget.hpp b/main/widgets/RoomCardWidget.hpp deleted file mode 100644 index 8d0daac..0000000 --- a/main/widgets/RoomCardWidget.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "Widget.hpp" - -class RoomCardWidget : public Widget { -public: - explicit RoomCardWidget(const WidgetConfig& config); - ~RoomCardWidget() override; - lv_obj_t* create(lv_obj_t* parent) override; - void applyStyle() override; - void onKnxValue(float value) override; // Temperature update - void onKnxSwitch(bool value) override; // Not used directly - - // Sub-button specific handling - void onSubButtonStatus(uint8_t index, bool value); - void sendSubButtonToggle(uint8_t index); - - // Override to also clear sub-buttons - void clearLvglObject(); - -private: - // Parent (screen) for sub-buttons to avoid clipping - lv_obj_t* screenParent_ = nullptr; - - // Bubble style elements (cardStyle=0) - lv_obj_t* bubble_ = nullptr; - lv_obj_t* roomIcon_ = nullptr; - lv_obj_t* roomLabel_ = nullptr; - lv_obj_t* tempLabel_ = nullptr; - - // Tile style elements (cardStyle=1) - lv_obj_t* subtitleLabel_ = nullptr; - lv_obj_t* humidityLabel_ = nullptr; - lv_obj_t* decorIcon_ = nullptr; // Large decorative icon bottom-left - - // Sub-button elements - lv_obj_t* subButtonObjs_[MAX_SUBBUTTONS] = {}; - lv_obj_t* subButtonIcons_[MAX_SUBBUTTONS] = {}; - bool subButtonStates_[MAX_SUBBUTTONS] = {}; - - // Cached config values - char roomName_[MAX_TEXT_LEN] = {0}; - char tempFormat_[MAX_TEXT_LEN] = {0}; - char subtitle_[MAX_TEXT_LEN] = {0}; - char humidityFormat_[MAX_FORMAT_LEN] = {0}; - - // Layout helpers - void parseText(); - void createBubbleLayout(); // Bubble style (round) - void createTileLayout(); // Tile style (rectangular) - void createSubButtons(); - void createSubButtonsTile(); // SubButtons for tile (right side) - void updateSubButtonColor(uint8_t index); - void updateTemperature(float value); - void updateHumidity(float value); - - // Geometry calculations - void calculateSubButtonPosition(SubButtonPosition pos, int16_t& x, int16_t& y); - void calculateSubButtonPositionTile(uint8_t index, int16_t& x, int16_t& y); - - // Event handlers - static void bubbleClickCallback(lv_event_t* e); - static void subButtonClickCallback(lv_event_t* e); - - // UTF-8 encoding helper - static int encodeUtf8(uint32_t codepoint, char* buf); -}; diff --git a/main/widgets/RoomCardWidgetBase.cpp b/main/widgets/RoomCardWidgetBase.cpp new file mode 100644 index 0000000..d8274ab --- /dev/null +++ b/main/widgets/RoomCardWidgetBase.cpp @@ -0,0 +1,231 @@ +#include "RoomCardWidgetBase.hpp" +#include "../Fonts.hpp" +#include "../WidgetManager.hpp" +#include "esp_log.h" +#include +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +RoomCardWidgetBase::RoomCardWidgetBase(const WidgetConfig& config) + : Widget(config) +{ + roomName_[0] = '\0'; + tempFormat_[0] = '\0'; + for (size_t i = 0; i < MAX_SUBBUTTONS; ++i) { + subButtonStates_[i] = false; + } +} + +RoomCardWidgetBase::~RoomCardWidgetBase() { + // Sub-buttons are on screen parent, not on obj_, so delete them explicitly + for (size_t i = 0; i < MAX_SUBBUTTONS; ++i) { + if (subButtonObjs_[i] && lv_obj_is_valid(subButtonObjs_[i])) { + lv_obj_delete(subButtonObjs_[i]); + subButtonObjs_[i] = nullptr; + } + } +} + +void RoomCardWidgetBase::clearLvglObject() { + for (size_t i = 0; i < MAX_SUBBUTTONS; ++i) { + subButtonObjs_[i] = nullptr; + subButtonIcons_[i] = nullptr; + } + roomLabel_ = nullptr; + tempLabel_ = nullptr; + obj_ = nullptr; +} + +int RoomCardWidgetBase::encodeUtf8(uint32_t codepoint, char* buf) { + if (codepoint < 0x80) { + buf[0] = static_cast(codepoint); + buf[1] = '\0'; + return 1; + } else if (codepoint < 0x800) { + buf[0] = static_cast(0xC0 | (codepoint >> 6)); + buf[1] = static_cast(0x80 | (codepoint & 0x3F)); + buf[2] = '\0'; + return 2; + } else if (codepoint < 0x10000) { + buf[0] = static_cast(0xE0 | (codepoint >> 12)); + buf[1] = static_cast(0x80 | ((codepoint >> 6) & 0x3F)); + buf[2] = static_cast(0x80 | (codepoint & 0x3F)); + buf[3] = '\0'; + return 3; + } else if (codepoint < 0x110000) { + buf[0] = static_cast(0xF0 | (codepoint >> 18)); + buf[1] = static_cast(0x80 | ((codepoint >> 12) & 0x3F)); + buf[2] = static_cast(0x80 | ((codepoint >> 6) & 0x3F)); + buf[3] = static_cast(0x80 | (codepoint & 0x3F)); + buf[4] = '\0'; + return 4; + } + buf[0] = '\0'; + return 0; +} + +void RoomCardWidgetBase::parseTextBase() { + roomName_[0] = '\0'; + tempFormat_[0] = '\0'; + + const char* text = config_.text; + if (text && text[0] != '\0') { + const char* newline = strchr(text, '\n'); + if (newline) { + size_t nameLen = static_cast(newline - text); + if (nameLen >= MAX_TEXT_LEN) nameLen = MAX_TEXT_LEN - 1; + memcpy(roomName_, text, nameLen); + roomName_[nameLen] = '\0'; + + strncpy(tempFormat_, newline + 1, MAX_TEXT_LEN - 1); + tempFormat_[MAX_TEXT_LEN - 1] = '\0'; + } else { + strncpy(roomName_, text, MAX_TEXT_LEN - 1); + roomName_[MAX_TEXT_LEN - 1] = '\0'; + } + } +} + +void RoomCardWidgetBase::createSubButtonsCommon() { + int16_t subBtnSize = config_.subButtonSize > 0 ? config_.subButtonSize : 40; + + for (uint8_t i = 0; i < config_.subButtonCount && i < MAX_SUBBUTTONS; ++i) { + const SubButtonConfig& cfg = config_.subButtons[i]; + if (!cfg.enabled) continue; + + // Create sub-button on screen parent (not on widget container) to avoid clipping + lv_obj_t* btn = lv_obj_create(screenParent_); + lv_obj_remove_style_all(btn); + lv_obj_set_size(btn, subBtnSize, subBtnSize); + lv_obj_clear_flag(btn, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_add_flag(btn, LV_OBJ_FLAG_CLICKABLE); + + // Circular shape + lv_obj_set_style_radius(btn, LV_RADIUS_CIRCLE, 0); + lv_obj_set_style_bg_opa(btn, config_.subButtonOpacity, 0); + + // Position using layout-specific calculation + int16_t relX, relY; + calculateSubButtonPosition(i, cfg, relX, relY); + int16_t absX = config_.x + relX; + int16_t absY = config_.y + relY; + lv_obj_set_pos(btn, absX, absY); + + // Create icon + if (cfg.iconCodepoint > 0 && Fonts::hasIconFont()) { + lv_obj_t* icon = lv_label_create(btn); + lv_obj_clear_flag(icon, LV_OBJ_FLAG_CLICKABLE); + char iconText[5]; + encodeUtf8(cfg.iconCodepoint, iconText); + lv_label_set_text(icon, iconText); + lv_obj_center(icon); + subButtonIcons_[i] = icon; + } + + // Store index in user_data for click handler + lv_obj_set_user_data(btn, reinterpret_cast(static_cast(i))); + lv_obj_add_event_cb(btn, subButtonClickCallback, LV_EVENT_CLICKED, this); + + subButtonObjs_[i] = btn; + subButtonStates_[i] = false; + + // Apply initial color (OFF state) + updateSubButtonColor(i); + } +} + +void RoomCardWidgetBase::applySubButtonStyle() { + uint8_t subBtnFontIdx = 1; // Default small + if (config_.subButtonSize > 55) { + subBtnFontIdx = 3; + } else if (config_.subButtonSize > 40) { + subBtnFontIdx = 2; + } + const lv_font_t* subBtnIconFont = Fonts::iconFont(subBtnFontIdx); + for (uint8_t i = 0; i < config_.subButtonCount && i < MAX_SUBBUTTONS; ++i) { + if (subButtonIcons_[i] && subBtnIconFont) { + lv_obj_set_style_text_font(subButtonIcons_[i], subBtnIconFont, 0); + lv_obj_set_style_text_color(subButtonIcons_[i], lv_color_white(), 0); + } + } +} + +void RoomCardWidgetBase::updateSubButtonColor(uint8_t index) { + if (index >= MAX_SUBBUTTONS || !subButtonObjs_[index]) return; + + const SubButtonConfig& cfg = config_.subButtons[index]; + const Color& color = subButtonStates_[index] ? cfg.colorOn : cfg.colorOff; + + lv_obj_set_style_bg_color(subButtonObjs_[index], lv_color_hex(color.toLvColor()), 0); +} + +void RoomCardWidgetBase::updateTemperature(float value) { + if (!tempLabel_ || tempFormat_[0] == '\0') return; + + char buf[32]; + snprintf(buf, sizeof(buf), tempFormat_, value); + lv_label_set_text(tempLabel_, buf); +} + +void RoomCardWidgetBase::onKnxValue(float value) { + cachedPrimaryValue_ = value; + hasCachedPrimary_ = true; + updateTemperature(value); +} + +void RoomCardWidgetBase::onKnxSwitch(bool value) { + // Not used directly - sub-button status is handled via onSubButtonStatus + (void)value; +} + +void RoomCardWidgetBase::onSubButtonStatus(uint8_t index, bool value) { + if (index >= MAX_SUBBUTTONS) return; + + subButtonStates_[index] = value; + updateSubButtonColor(index); +} + +void RoomCardWidgetBase::sendSubButtonToggle(uint8_t index) { + if (index >= config_.subButtonCount || index >= MAX_SUBBUTTONS) return; + + const SubButtonConfig& cfg = config_.subButtons[index]; + if (cfg.action == SubButtonAction::TOGGLE_KNX && cfg.knxAddrWrite > 0) { + bool newState = !subButtonStates_[index]; + WidgetManager::instance().sendKnxSwitch(cfg.knxAddrWrite, newState); + subButtonStates_[index] = newState; + updateSubButtonColor(index); + } +} + +void RoomCardWidgetBase::cardClickCallback(lv_event_t* e) { + RoomCardWidgetBase* widget = static_cast(lv_event_get_user_data(e)); + if (!widget) return; + + if (widget->config_.action == ButtonAction::JUMP) { + WidgetManager::instance().navigateToScreen(widget->config_.targetScreen); + } else if (widget->config_.action == ButtonAction::BACK) { + WidgetManager::instance().navigateBack(); + } +} + +void RoomCardWidgetBase::subButtonClickCallback(lv_event_t* e) { + RoomCardWidgetBase* widget = static_cast(lv_event_get_user_data(e)); + lv_obj_t* target = static_cast(lv_event_get_target(e)); + if (!widget || !target) return; + + uint8_t index = static_cast(reinterpret_cast(lv_obj_get_user_data(target))); + + if (index < widget->config_.subButtonCount && index < MAX_SUBBUTTONS) { + const SubButtonConfig& cfg = widget->config_.subButtons[index]; + + if (cfg.action == SubButtonAction::TOGGLE_KNX) { + widget->sendSubButtonToggle(index); + } else if (cfg.action == SubButtonAction::NAVIGATE) { + WidgetManager::instance().navigateToScreen(cfg.targetScreen); + } + } +} diff --git a/main/widgets/RoomCardWidgetBase.hpp b/main/widgets/RoomCardWidgetBase.hpp new file mode 100644 index 0000000..7c5af6b --- /dev/null +++ b/main/widgets/RoomCardWidgetBase.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include "Widget.hpp" + +class RoomCardWidgetBase : public Widget { +public: + explicit RoomCardWidgetBase(const WidgetConfig& config); + ~RoomCardWidgetBase() override; + + void onKnxValue(float value) override; // Temperature update + void onKnxSwitch(bool value) override; // Not used directly + + // Sub-button specific handling + void onSubButtonStatus(uint8_t index, bool value); + void sendSubButtonToggle(uint8_t index); + + // Clear LVGL objects + virtual void clearLvglObject(); + +protected: + // Parent (screen) for sub-buttons to avoid clipping + lv_obj_t* screenParent_ = nullptr; + + // Common elements + lv_obj_t* roomLabel_ = nullptr; + lv_obj_t* tempLabel_ = nullptr; + + // Sub-button elements + lv_obj_t* subButtonObjs_[MAX_SUBBUTTONS] = {}; + lv_obj_t* subButtonIcons_[MAX_SUBBUTTONS] = {}; + bool subButtonStates_[MAX_SUBBUTTONS] = {}; + + // Cached config values + char roomName_[MAX_TEXT_LEN] = {0}; + char tempFormat_[MAX_TEXT_LEN] = {0}; + + // Common helpers + void parseTextBase(); + void updateSubButtonColor(uint8_t index); + void updateTemperature(float value); + + // Virtual methods for layout-specific sub-button positioning + virtual void calculateSubButtonPosition(uint8_t index, const SubButtonConfig& cfg, int16_t& x, int16_t& y) = 0; + void createSubButtonsCommon(); + void applySubButtonStyle(); + + // Event handlers + static void cardClickCallback(lv_event_t* e); + static void subButtonClickCallback(lv_event_t* e); + + // UTF-8 encoding helper + static int encodeUtf8(uint32_t codepoint, char* buf); +}; diff --git a/main/widgets/WidgetFactory.cpp b/main/widgets/WidgetFactory.cpp index 9ded5dd..c3a06ad 100644 --- a/main/widgets/WidgetFactory.cpp +++ b/main/widgets/WidgetFactory.cpp @@ -10,7 +10,8 @@ #include "PowerLinkWidget.hpp" #include "ChartWidget.hpp" #include "ClockWidget.hpp" -#include "RoomCardWidget.hpp" +#include "RoomCardBubbleWidget.hpp" +#include "RoomCardTileWidget.hpp" std::unique_ptr WidgetFactory::create(const WidgetConfig& config) { if (!config.visible) return nullptr; @@ -39,7 +40,11 @@ std::unique_ptr WidgetFactory::create(const WidgetConfig& config) { case WidgetType::CLOCK: return std::make_unique(config); case WidgetType::ROOMCARD: - return std::make_unique(config); + // cardStyle: 0=Bubble, 1=Tile + if (config.cardStyle == 1) { + return std::make_unique(config); + } + return std::make_unique(config); default: return nullptr; } diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 9af871b..38a0926 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -10,8 +10,15 @@ CONFIG_LV_FONT_MONTSERRAT_48=y # Keep LVGL draw thread stack reasonable to avoid xTaskCreate failures CONFIG_LV_DRAW_THREAD_STACK_SIZE=32768 -# Increase LVGL heap to avoid draw task OOM with dynamic UI -CONFIG_LV_MEM_SIZE_KILOBYTES=128 +# Use C library malloc instead of LVGL builtin - enables PSRAM usage +CONFIG_LV_USE_BUILTIN_MALLOC=n +CONFIG_LV_USE_CLIB_MALLOC=y + +# Increase draw layer buffer for large icons (250px icons need ~120KB) +CONFIG_LV_DRAW_LAYER_SIMPLE_BUF_SIZE=131072 + +# Backup LVGL heap size (used if builtin malloc is enabled) +CONFIG_LV_MEM_SIZE_KILOBYTES=512 # Enable object names for LVGL debug mapping CONFIG_LV_USE_OBJ_NAME=y @@ -22,3 +29,10 @@ CONFIG_ESP_LVGL_ADAPTER_ENABLE_FREETYPE=y # Increase main task stack for FreeType (needs ~16KB+) CONFIG_ESP_MAIN_TASK_STACK_SIZE=16384 + +# PSRAM configuration for large allocations +# Allocations > 1KB go to PSRAM to avoid internal RAM exhaustion +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=1024 + +# Allow PSRAM as malloc target +CONFIG_SPIRAM_USE_MALLOC=y diff --git a/web-interface/src/components/CanvasArea.vue b/web-interface/src/components/CanvasArea.vue index 87eb246..45a741b 100644 --- a/web-interface/src/components/CanvasArea.vue +++ b/web-interface/src/components/CanvasArea.vue @@ -282,7 +282,12 @@ function resizeDrag(e) { let newW = Math.round(rawW); let newH = Math.round(rawH); - if (w.type === WIDGET_TYPES.LED || w.type === WIDGET_TYPES.POWERNODE || w.type === WIDGET_TYPES.ROOMCARD) { + // Force square for LED, PowerNode, and RoomCard Bubble (not Tile) + const forceSquare = w.type === WIDGET_TYPES.LED || + w.type === WIDGET_TYPES.POWERNODE || + (w.type === WIDGET_TYPES.ROOMCARD && w.cardStyle !== 1); + + if (forceSquare) { const maxSize = Math.min(maxW, maxH); const size = clamp(Math.max(newW, newH), minSize.w, maxSize); newW = size; diff --git a/web-interface/src/components/SidebarRight.vue b/web-interface/src/components/SidebarRight.vue index 123a805..0aa0845 100644 --- a/web-interface/src/components/SidebarRight.vue +++ b/web-interface/src/components/SidebarRight.vue @@ -6,7 +6,7 @@ Waehle ein Widget im Canvas oder im Baum.

- +

Layout

@@ -14,640 +14,19 @@
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +
- +
@@ -662,303 +41,114 @@ diff --git a/web-interface/src/components/WidgetElement.vue b/web-interface/src/components/WidgetElement.vue index c526945..1c4d1cf 100644 --- a/web-interface/src/components/WidgetElement.vue +++ b/web-interface/src/components/WidgetElement.vue @@ -1,868 +1,56 @@ diff --git a/web-interface/src/components/widgets/elements/ButtonElement.vue b/web-interface/src/components/widgets/elements/ButtonElement.vue new file mode 100644 index 0000000..1294e1c --- /dev/null +++ b/web-interface/src/components/widgets/elements/ButtonElement.vue @@ -0,0 +1,132 @@ + + + diff --git a/web-interface/src/components/widgets/elements/ChartElement.vue b/web-interface/src/components/widgets/elements/ChartElement.vue new file mode 100644 index 0000000..e0130ac --- /dev/null +++ b/web-interface/src/components/widgets/elements/ChartElement.vue @@ -0,0 +1,67 @@ + + + diff --git a/web-interface/src/components/widgets/elements/ClockElement.vue b/web-interface/src/components/widgets/elements/ClockElement.vue new file mode 100644 index 0000000..d98da7b --- /dev/null +++ b/web-interface/src/components/widgets/elements/ClockElement.vue @@ -0,0 +1,65 @@ + + + diff --git a/web-interface/src/components/widgets/elements/IconElement.vue b/web-interface/src/components/widgets/elements/IconElement.vue new file mode 100644 index 0000000..94e35e4 --- /dev/null +++ b/web-interface/src/components/widgets/elements/IconElement.vue @@ -0,0 +1,75 @@ + + + diff --git a/web-interface/src/components/widgets/elements/LabelElement.vue b/web-interface/src/components/widgets/elements/LabelElement.vue new file mode 100644 index 0000000..14f64c3 --- /dev/null +++ b/web-interface/src/components/widgets/elements/LabelElement.vue @@ -0,0 +1,112 @@ + + + diff --git a/web-interface/src/components/widgets/elements/LedElement.vue b/web-interface/src/components/widgets/elements/LedElement.vue new file mode 100644 index 0000000..f240ea0 --- /dev/null +++ b/web-interface/src/components/widgets/elements/LedElement.vue @@ -0,0 +1,63 @@ + + + diff --git a/web-interface/src/components/widgets/elements/PowerFlowElement.vue b/web-interface/src/components/widgets/elements/PowerFlowElement.vue new file mode 100644 index 0000000..c3d0f0c --- /dev/null +++ b/web-interface/src/components/widgets/elements/PowerFlowElement.vue @@ -0,0 +1,201 @@ + + + diff --git a/web-interface/src/components/widgets/elements/PowerNodeElement.vue b/web-interface/src/components/widgets/elements/PowerNodeElement.vue new file mode 100644 index 0000000..25af34e --- /dev/null +++ b/web-interface/src/components/widgets/elements/PowerNodeElement.vue @@ -0,0 +1,132 @@ + + + diff --git a/web-interface/src/components/widgets/elements/RoomCardElement.vue b/web-interface/src/components/widgets/elements/RoomCardElement.vue new file mode 100644 index 0000000..a16a9e4 --- /dev/null +++ b/web-interface/src/components/widgets/elements/RoomCardElement.vue @@ -0,0 +1,287 @@ + + + diff --git a/web-interface/src/components/widgets/elements/TabPageElement.vue b/web-interface/src/components/widgets/elements/TabPageElement.vue new file mode 100644 index 0000000..5328135 --- /dev/null +++ b/web-interface/src/components/widgets/elements/TabPageElement.vue @@ -0,0 +1,61 @@ + + + diff --git a/web-interface/src/components/widgets/elements/TabViewElement.vue b/web-interface/src/components/widgets/elements/TabViewElement.vue new file mode 100644 index 0000000..3284a56 --- /dev/null +++ b/web-interface/src/components/widgets/elements/TabViewElement.vue @@ -0,0 +1,126 @@ + + + diff --git a/web-interface/src/components/widgets/settings/ButtonSettings.vue b/web-interface/src/components/widgets/settings/ButtonSettings.vue new file mode 100644 index 0000000..b5547f1 --- /dev/null +++ b/web-interface/src/components/widgets/settings/ButtonSettings.vue @@ -0,0 +1,81 @@ + + + diff --git a/web-interface/src/components/widgets/settings/ChartSettings.vue b/web-interface/src/components/widgets/settings/ChartSettings.vue new file mode 100644 index 0000000..10549c5 --- /dev/null +++ b/web-interface/src/components/widgets/settings/ChartSettings.vue @@ -0,0 +1,108 @@ + + + diff --git a/web-interface/src/components/widgets/settings/ClockSettings.vue b/web-interface/src/components/widgets/settings/ClockSettings.vue new file mode 100644 index 0000000..3548fe2 --- /dev/null +++ b/web-interface/src/components/widgets/settings/ClockSettings.vue @@ -0,0 +1,26 @@ + + + diff --git a/web-interface/src/components/widgets/settings/IconSettings.vue b/web-interface/src/components/widgets/settings/IconSettings.vue new file mode 100644 index 0000000..932a373 --- /dev/null +++ b/web-interface/src/components/widgets/settings/IconSettings.vue @@ -0,0 +1,66 @@ + + + diff --git a/web-interface/src/components/widgets/settings/LabelSettings.vue b/web-interface/src/components/widgets/settings/LabelSettings.vue new file mode 100644 index 0000000..1d4260a --- /dev/null +++ b/web-interface/src/components/widgets/settings/LabelSettings.vue @@ -0,0 +1,113 @@ + + + diff --git a/web-interface/src/components/widgets/settings/LedSettings.vue b/web-interface/src/components/widgets/settings/LedSettings.vue new file mode 100644 index 0000000..f88eef4 --- /dev/null +++ b/web-interface/src/components/widgets/settings/LedSettings.vue @@ -0,0 +1,55 @@ + + + diff --git a/web-interface/src/components/widgets/settings/PowerFlowSettings.vue b/web-interface/src/components/widgets/settings/PowerFlowSettings.vue new file mode 100644 index 0000000..9f5978f --- /dev/null +++ b/web-interface/src/components/widgets/settings/PowerFlowSettings.vue @@ -0,0 +1,175 @@ + + + diff --git a/web-interface/src/components/widgets/settings/PowerNodeSettings.vue b/web-interface/src/components/widgets/settings/PowerNodeSettings.vue new file mode 100644 index 0000000..4b677cb --- /dev/null +++ b/web-interface/src/components/widgets/settings/PowerNodeSettings.vue @@ -0,0 +1,258 @@ + + + diff --git a/web-interface/src/components/widgets/settings/RoomCardSettings.vue b/web-interface/src/components/widgets/settings/RoomCardSettings.vue new file mode 100644 index 0000000..81abc56 --- /dev/null +++ b/web-interface/src/components/widgets/settings/RoomCardSettings.vue @@ -0,0 +1,339 @@ + + + diff --git a/web-interface/src/components/widgets/settings/TabPageSettings.vue b/web-interface/src/components/widgets/settings/TabPageSettings.vue new file mode 100644 index 0000000..9d2a893 --- /dev/null +++ b/web-interface/src/components/widgets/settings/TabPageSettings.vue @@ -0,0 +1,30 @@ + + + diff --git a/web-interface/src/components/widgets/settings/TabViewSettings.vue b/web-interface/src/components/widgets/settings/TabViewSettings.vue new file mode 100644 index 0000000..4e96eb3 --- /dev/null +++ b/web-interface/src/components/widgets/settings/TabViewSettings.vue @@ -0,0 +1,41 @@ + + + diff --git a/web-interface/src/components/widgets/shared/BaseWidget.vue b/web-interface/src/components/widgets/shared/BaseWidget.vue new file mode 100644 index 0000000..cfc9a0b --- /dev/null +++ b/web-interface/src/components/widgets/shared/BaseWidget.vue @@ -0,0 +1,67 @@ + + + diff --git a/web-interface/src/components/widgets/shared/styles.js b/web-interface/src/components/widgets/shared/styles.js new file mode 100644 index 0000000..98e646e --- /dev/null +++ b/web-interface/src/components/widgets/shared/styles.js @@ -0,0 +1,11 @@ +// Shared CSS classes for settings components +export const rowClass = 'flex items-center gap-2.5 mb-2'; +export const labelClass = 'w-[90px] text-[12px] text-muted'; +export const inputClass = 'flex-1 bg-panel-2 border border-border rounded-lg px-2 py-1.5 text-text text-[12px] focus:outline-none focus:border-accent'; +export const headingClass = 'mt-4 mb-2.5 text-[12px] uppercase tracking-[0.08em] text-[#3a5f88]'; +export const subHeadingClass = 'mt-3 mb-2 text-[11px] uppercase tracking-[0.06em] text-[#5a7f9a]'; +export const noteClass = 'text-[11px] text-muted leading-tight'; +export const iconSelectClass = 'flex-1 bg-panel-2 border border-border rounded-lg px-2.5 py-1.5 text-text text-[12px] flex items-center justify-center gap-1.5 cursor-pointer hover:bg-[#e4ebf2] hover:border-[#b8c4d2]'; +export const iconRemoveClass = 'w-7 h-7 rounded-md border border-red-200 bg-[#f7dede] text-[#b3261e] grid place-items-center text-[12px] cursor-pointer hover:bg-[#f2cfcf]'; +export const colorInputClass = 'h-[30px] w-[44px] cursor-pointer border-0 bg-transparent p-0'; +export const smallColorInputClass = 'h-[22px] w-[32px] cursor-pointer border-0 bg-transparent p-0'; diff --git a/web-interface/src/components/widgets/shared/utils.js b/web-interface/src/components/widgets/shared/utils.js new file mode 100644 index 0000000..a088dc5 --- /dev/null +++ b/web-interface/src/components/widgets/shared/utils.js @@ -0,0 +1,58 @@ +// Shared utility functions for widget rendering +import { fontSizes } from '../../../constants'; +import { clamp, hexToRgba } from '../../../utils'; + +export { clamp, hexToRgba }; + +// Text alignment helpers +export function justifyForAlign(align) { + if (align === 0) return 'flex-start'; + if (align === 2) return 'flex-end'; + return 'center'; +} + +export function textAlignCss(align) { + if (align === 0) return 'left'; + if (align === 2) return 'right'; + return 'center'; +} + +// Base style for all widgets +export function getBaseStyle(widget, scale) { + return { + left: `${widget.x * scale}px`, + top: `${widget.y * scale}px`, + width: `${widget.w * scale}px`, + height: `${widget.h * scale}px`, + fontSize: `${(fontSizes[widget.fontSize] || 14) * scale}px`, + color: widget.textColor, + position: 'absolute', + zIndex: 1, + cursor: 'move', + userSelect: 'none', + touchAction: 'none' + }; +} + +// Shadow style helper +export function getShadowStyle(widget, scale) { + if (!widget.shadow || !widget.shadow.enabled) return {}; + + const sx = (widget.shadow.x || 0) * scale; + const sy = (widget.shadow.y || 0) * scale; + const blur = (widget.shadow.blur || 0) * scale; + const spread = (widget.shadow.spread || 0) * scale; + + return { + boxShadow: `${sx}px ${sy}px ${blur}px ${spread}px ${widget.shadow.color}` + }; +} + +// PowerNode text splitting +export function splitPowerNodeText(text) { + if (typeof text !== 'string') return { label: '', value: '' }; + const parts = text.split('\n'); + const label = parts[0] ?? ''; + const value = parts.slice(1).join('\n'); + return { label, value }; +} diff --git a/web-interface/src/constants.js b/web-interface/src/constants.js index 6f33832..536ca3f 100644 --- a/web-interface/src/constants.js +++ b/web-interface/src/constants.js @@ -155,6 +155,10 @@ export const ICON_DEFAULTS = { export const fontSizes = [14, 18, 22, 28, 36, 48]; +// Icon fonts have additional larger sizes for decorative use +// Large icons use PSRAM for draw buffers (ESP32-P4 with 32MB PSRAM) +export const iconFontSizes = [14, 18, 22, 28, 36, 48, 64, 80, 96, 120, 150, 180, 220, 260]; + export const defaultFormats = { 1: '%.1f °C', 2: '%s', @@ -441,7 +445,7 @@ export const WIDGET_DEFAULTS = { textColor: '#223447', bgColor: '#FFFFFF', bgOpacity: 255, - radius: 100, + radius: 16, // For Tile style (Bubble ignores this) shadow: { enabled: true, x: 0, y: 4, blur: 12, spread: 0, color: '#00000022' }, isToggle: false, knxAddrWrite: 0, @@ -456,6 +460,7 @@ export const WIDGET_DEFAULTS = { subButtonDistance: 80, // Distance from center in pixels subButtonOpacity: 255, // Sub-button opacity (0-255) cardStyle: 0, // 0=Bubble (round), 1=Tile (rectangular) - subButtons: [] + subButtons: [], + textLines: [] // Variable text lines with icon, text, textSrc, knxAddr, fontSize } }; diff --git a/web-interface/src/stores/editor.js b/web-interface/src/stores/editor.js index 961df96..5ad5a67 100644 --- a/web-interface/src/stores/editor.js +++ b/web-interface/src/stores/editor.js @@ -168,7 +168,9 @@ export const useEditorStore = defineStore('editor', () => { iconCodepoint: w.iconCodepoint || 0, iconPosition: w.iconPosition ?? defaults.iconPosition, iconSize: w.iconSize ?? defaults.iconSize, - iconGap: w.iconGap ?? defaults.iconGap + iconGap: w.iconGap ?? defaults.iconGap, + iconPositionX: w.iconPositionX ?? defaults.iconPositionX, + iconPositionY: w.iconPositionY ?? defaults.iconPositionY, }; screen.widgets.push(label); @@ -408,7 +410,9 @@ export const useEditorStore = defineStore('editor', () => { iconCodepoint: defaults.iconCodepoint || 0, iconPosition: defaults.iconPosition || 0, iconSize: defaults.iconSize || 1, - iconGap: defaults.iconGap || 8 + iconGap: defaults.iconGap || 8, + iconPositionX: defaults.iconPositionX || 8, + iconPositionY: defaults.iconPositionY || 8 }; if (defaults.chart) { @@ -429,9 +433,10 @@ export const useEditorStore = defineStore('editor', () => { w.conditions = []; } - // RoomCard: initialize sub-buttons array + // RoomCard: initialize sub-buttons and text lines arrays if (typeStr === 'roomcard') { w.subButtons = []; + w.textLines = []; } activeScreen.value.widgets.push(w); @@ -465,7 +470,9 @@ export const useEditorStore = defineStore('editor', () => { iconCodepoint: w.iconCodepoint || 0, iconPosition: w.iconPosition || 0, iconSize: w.iconSize || 1, - iconGap: w.iconGap || 8 + iconGap: w.iconGap || 8, + iconPositionX: w.iconPositionX || 8, + iconPositionY: w.iconPositionY || 8 }; activeScreen.value.widgets.push(label); } @@ -526,7 +533,9 @@ export const useEditorStore = defineStore('editor', () => { iconCodepoint: defaults.iconCodepoint || 0, iconPosition: defaults.iconPosition || 0, iconSize: defaults.iconSize || 0, - iconGap: defaults.iconGap || 0 + iconGap: defaults.iconGap || 0, + iconPositionX: defaults.iconPositionX || 0, + iconPositionY: defaults.iconPositionY || 0, }; activeScreen.value.widgets.push(link); diff --git a/web-interface/src/utils.js b/web-interface/src/utils.js index 3b69e46..d7fc8ac 100644 --- a/web-interface/src/utils.js +++ b/web-interface/src/utils.js @@ -71,6 +71,19 @@ export function normalizeWidget(w, nextWidgetIdRef) { } } + // TextLines: ensure each widget has its own array and each line has fontSize + if (defaults.textLines !== undefined) { + if (!Array.isArray(w.textLines)) { + w.textLines = []; + } + // Ensure each textLine has a fontSize + w.textLines.forEach(tl => { + if (tl.fontSize === undefined || tl.fontSize === null) { + tl.fontSize = 1; // Default 18px + } + }); + } + if (!w.shadow) { w.shadow = { ...defaults.shadow }; } else { @@ -126,6 +139,8 @@ export function normalizeWidget(w, nextWidgetIdRef) { if (w.iconPosition === undefined) w.iconPosition = defaults.iconPosition || 0; if (w.iconSize === undefined) w.iconSize = defaults.iconSize || 1; if (w.iconGap === undefined) w.iconGap = defaults.iconGap || 8; + if (w.iconPositionX === undefined) w.iconPositionX = defaults.iconPositionX || 0; + if (w.iconPositionY === undefined) w.iconPositionY = defaults.iconPositionY || 0; // Hierarchy if (w.parentId === undefined) w.parentId = -1;