CDiandanNatureItemUI.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. #include "pch.h"
  2. #include "CDiandanNatureItemUI.h"
  3. #include "../wnd/CDiandanNatureWnd.h"
  4. namespace
  5. {
  6. // 测量指定字体下文本的像素宽度(GDI,用于 mini-html {x offset} 右对齐加价)
  7. int MeasureTextWidth(DuiLib::CPaintManagerUI* pManager, int fontId, const std::wstring& text)
  8. {
  9. if (!pManager || text.empty()) return 0;
  10. DuiLib::TFontInfo* pFontInfo = pManager->GetFontInfo(fontId);
  11. if (!pFontInfo || !pFontInfo->hFont) return 0;
  12. HDC hdc = GetDC(NULL);
  13. if (!hdc) return 0;
  14. HDC memDC = CreateCompatibleDC(hdc);
  15. int width = 0;
  16. if (memDC)
  17. {
  18. HGDIOBJ hOldFont = SelectObject(memDC, pFontInfo->hFont);
  19. SIZE size = { 0, 0 };
  20. if (GetTextExtentPoint32W(memDC, text.c_str(), (int)text.length(), &size))
  21. {
  22. width = size.cx;
  23. }
  24. SelectObject(memDC, hOldFont);
  25. DeleteDC(memDC);
  26. }
  27. ReleaseDC(NULL, hdc);
  28. return width;
  29. }
  30. // 去掉属性值名称末尾的加价后缀(如 +3 / +27 / +0.5),避免与右侧红橙价重复
  31. // 规则:尾部空格 + +号 + 可选小数数字;若去掉后为空则保留原名,防止无名称
  32. std::wstring StripTrailingPrice(const std::wstring& name)
  33. {
  34. if (name.empty()) return name;
  35. int n = (int)name.size();
  36. int i = n - 1;
  37. while (i >= 0 && name[i] == L' ') i--;
  38. if (i < 0) return name;
  39. // 必须以数字结尾
  40. if (i >= 0 && name[i] >= L'0' && name[i] <= L'9')
  41. {
  42. // 整数部分
  43. while (i >= 0 && name[i] >= L'0' && name[i] <= L'9') i--;
  44. // 可选小数部分:.digits
  45. if (i >= 0 && name[i] == L'.')
  46. {
  47. int dotPos = i;
  48. int j = i - 1;
  49. while (j >= 0 && name[j] >= L'0' && name[j] <= L'9') j--;
  50. if (j < dotPos - 1) i = j; // 小数点后有数字,一起去掉
  51. }
  52. // 前面必须是 '+'
  53. if (i >= 0 && name[i] == L'+')
  54. {
  55. int cut = i;
  56. while (cut > 0 && name[cut - 1] == L' ') cut--;
  57. if (cut > 0) return name.substr(0, cut);
  58. // 整个字符串只剩下 +N 时保留原名,避免按钮名字为空
  59. return name;
  60. }
  61. }
  62. return name;
  63. }
  64. }
  65. CDiandanNatureItemUI::CDiandanNatureItemUI()
  66. {
  67. }
  68. CDiandanNatureItemUI::~CDiandanNatureItemUI()
  69. {
  70. }
  71. void CDiandanNatureItemUI::SetNatureName(std::string name)
  72. {
  73. m_nature_name = name;
  74. CLabelUI* pName = static_cast<CLabelUI*>(this->FindSubControl(_T("diandan_nature_layout_name")));
  75. pName->SetText(CLewaimaiString::UTF8ToUnicode(name).c_str());
  76. }
  77. void CDiandanNatureItemUI::SetNatureLimitChoose(std::string limit_choose)
  78. {
  79. m_nLimitChoose = atoi(limit_choose.c_str());
  80. }
  81. void CDiandanNatureItemUI::SetNatureNameNum(int nNum)
  82. {
  83. m_nNatureNameNum = nNum;
  84. }
  85. void CDiandanNatureItemUI::addNatureValue(FoodNatureValue value)
  86. {
  87. m_nature_value.push_back(value);
  88. }
  89. std::wstring CDiandanNatureItemUI::MakeBtnText(DuiLib::CPaintManagerUI* pManager, const std::wstring& name, int value, bool selected)
  90. {
  91. // 去掉商品属性值名称末尾自带的加价后缀(如 "+3"),避免与右侧红橙价重复显示
  92. std::wstring baseName = StripTrailingPrice(name);
  93. if (baseName.empty()) baseName = name;
  94. std::wstring s = baseName;
  95. if (value > 0)
  96. {
  97. // 文本区宽度 = 按钮 185 - 左 padding 12 - 右 padding 12 - 右侧安全边距 2
  98. const int TEXT_AREA_W = 185 - 12 - 12 - 2;
  99. int nameFontId = selected ? 112 : 12; // 选中 13 粗体 / 未选 13 常规
  100. int priceFontId = 112; // 加价始终 13px 粗体(与属性名同字号;选中/未选中都加粗)
  101. // 加价:暖红橙 #F5623D + 13 粗体,与属性名同字号但恒定加粗
  102. // 数字前加人民币符号 ¥(紧贴数字,+ 为加价标识)
  103. std::wstring price = L"+¥" + std::to_wstring(value);
  104. int priceW = MeasureTextWidth(pManager, priceFontId, price);
  105. const int MIN_GAP = 4; // 名称与加价之间的最小间距
  106. // 名称超长时截断并加省略号,保证加价始终完整可见(不会被右侧 padding 裁掉)
  107. std::wstring showName = baseName;
  108. int nameW = MeasureTextWidth(pManager, nameFontId, showName);
  109. int availForName = TEXT_AREA_W - priceW - MIN_GAP;
  110. if (nameW > availForName)
  111. {
  112. std::wstring trunc = baseName;
  113. while (trunc.length() > 1)
  114. {
  115. trunc = trunc.substr(0, trunc.length() - 1);
  116. if (MeasureTextWidth(pManager, nameFontId, trunc + L"…") <= availForName) break;
  117. }
  118. showName = trunc + L"…";
  119. nameW = MeasureTextWidth(pManager, nameFontId, showName);
  120. }
  121. int offset = TEXT_AREA_W - nameW - priceW;
  122. if (offset < MIN_GAP) offset = MIN_GAP; // 兜底间距,防止极端情况下重叠
  123. // 仅改红色 + 强制 13 粗体(选中/未选中都加粗,font 112),与属性名同字号
  124. s = showName + L"{x " + std::to_wstring(offset) + L"}<c #F5623D><f 112>" + price + L"</f></c>";
  125. }
  126. return s;
  127. }
  128. void CDiandanNatureItemUI::ApplySelectStyle(CButtonUI* pBtn, int index, bool selected)
  129. {
  130. DuiLib::CPaintManagerUI* pManager = pBtn->GetManager();
  131. if (selected)
  132. {
  133. // C3 选中态:白底 + 主绿描边 + 绿字
  134. pBtn->SetNormalImage(L"file='nature_pill_sel_n.png'");
  135. pBtn->SetHotImage(L"file='nature_pill_sel_h.png'");
  136. pBtn->SetPushedImage(L"file='nature_pill_sel_p.png'");
  137. pBtn->SetTextColor(0xFF60B177);
  138. pBtn->SetHotTextColor(0xFF60B177);
  139. pBtn->SetPushedTextColor(0xFF60B177);
  140. pBtn->SetFont(112);
  141. }
  142. else
  143. {
  144. // C3 未选中态:白底 + 浅灰描边 + 深色字
  145. pBtn->SetNormalImage(L"file='nature_pill_n.png'");
  146. pBtn->SetHotImage(L"file='nature_pill_h.png'");
  147. pBtn->SetPushedImage(L"file='nature_pill_p.png'");
  148. pBtn->SetTextColor(0xFF1F2329);
  149. pBtn->SetHotTextColor(0xFF1F2329);
  150. pBtn->SetPushedTextColor(0xFF1F2329);
  151. pBtn->SetFont(12);
  152. }
  153. FoodNatureValue& v = m_nature_value[index];
  154. pBtn->SetText(MakeBtnText(pManager, CLewaimaiString::UTF8ToUnicode(v.name), v.value, selected).c_str());
  155. pBtn->Invalidate();
  156. }
  157. void CDiandanNatureItemUI::ShowNatureValue()
  158. {
  159. CHorizontalLayoutUI* pValueLayout = static_cast<CHorizontalLayoutUI*>(this->FindSubControl(_T("diandan_nature_layout_value")));
  160. int totoal_value_size = m_nature_value.size();
  161. int nRows = (totoal_value_size - 1) / 4 + 1;
  162. int nCols = 4;
  163. //设置属性区域的高度
  164. pValueLayout->SetFixedHeight(nRows * 54);
  165. // 组高 = 网格高 + 分组头 28 + 头与网格间距 12(childpadding) + 顶 padding 10
  166. // 注意:XML 中 padding top=10 会减少内容区高度,必须同步补回来,否则最后一行底部被截断
  167. this->SetFixedHeight(nRows * 54 + 28 + 12 + 10);
  168. //开始设置每个控件的位置
  169. int value_num = 0;
  170. for (std::vector<FoodNatureValue>::iterator it = m_nature_value.begin(); it != m_nature_value.end(); it++)
  171. {
  172. //这里面的是属性值信息
  173. CButtonUI* newButton = new CButtonUI();
  174. newButton->SetFixedWidth(185);
  175. newButton->SetFixedHeight(44);
  176. newButton->SetShowHtml(true);
  177. newButton->SetTextPadding(RECT{ 12, 0, 12, 0 });
  178. newButton->SetBorderSize(0);
  179. newButton->SetBkColor(0);
  180. newButton->SetHotBkColor(0);
  181. newButton->SetPushedBkColor(0);
  182. //设置定义属性,在点击按钮的时候好区分
  183. newButton->SetAttribute(L"name", L"diandan_nature_value_btn");
  184. newButton->AddCustomAttribute(L"nature_name_num", std::to_wstring(m_nNatureNameNum).c_str());
  185. newButton->AddCustomAttribute(L"nature_value_num", std::to_wstring(value_num).c_str());
  186. //必须先Add,才能调用下面的SetPos,否则无效
  187. pValueLayout->Add(newButton);
  188. //设置位置,根据行数和列数来计算,行数和列数第一个都是1,按钮宽度185,高度44,右和下间距10
  189. int curRow = value_num / 4 + 1;
  190. int curCol = value_num % 4 + 1;
  191. RECT rect;
  192. rect.left = (curCol - 1) * 195;
  193. rect.right = rect.left + 185;
  194. rect.top = (curRow - 1) * 54;
  195. rect.bottom = rect.top + 44;
  196. newButton->SetFloat(true);
  197. // 强制设置固定大小和位置
  198. SIZE size;
  199. size.cx = rect.left;
  200. size.cy = rect.top;
  201. newButton->SetFixedXY(size);
  202. newButton->SetFixedWidth(rect.right - rect.left);
  203. newButton->SetFixedHeight(rect.bottom - rect.top);
  204. //默认未选中样式(C3 白底描边 + 右对齐加价)
  205. ApplySelectStyle(newButton, value_num, false);
  206. value_num++;
  207. }
  208. //初始化选中
  209. //如果是点击商品的,默认选中第一个
  210. if (m_InitFoodNatureSelectValue.m_nature_selects.size() == 0)
  211. {
  212. m_nature_value[0].is_select = true;
  213. CButtonUI* newButton = static_cast<CButtonUI*>(pValueLayout->GetItemAt(0));
  214. ApplySelectStyle(newButton, 0, true);
  215. m_nSelectNum = 1;
  216. }
  217. else
  218. {
  219. m_nSelectNum = 0;
  220. //可能有1到多个商品属性值被选中
  221. for (std::vector< FoodNatureSelectValueInfo>::iterator it = m_InitFoodNatureSelectValue.m_nature_selects.begin(); it != m_InitFoodNatureSelectValue.m_nature_selects.end(); it++)
  222. {
  223. int index = (*it).nature_select_index;
  224. m_nature_value[index].is_select = true;
  225. CButtonUI* newButton = static_cast<CButtonUI*>(pValueLayout->GetItemAt(index));
  226. ApplySelectStyle(newButton, index, true);
  227. m_nSelectNum++;
  228. }
  229. }
  230. }
  231. void CDiandanNatureItemUI::SetNatureSelected(int index)
  232. {
  233. CHorizontalLayoutUI* pValueLayout = static_cast<CHorizontalLayoutUI*>(this->FindSubControl(_T("diandan_nature_layout_value")));
  234. if (m_nature_value[index].is_select == true)
  235. {
  236. //点击的这个当前是选中状态
  237. if (m_nLimitChoose == 1)
  238. {
  239. //如果最多就只能选一个属性值的,点了选中状态的就没变化
  240. }
  241. else
  242. {
  243. //如果是可以选多个值的,点了选中状态就改为非选中状态
  244. if (m_nSelectNum == 1)
  245. {
  246. m_pNatureWnd->ShowToast(L"最少选择1个属性");
  247. return;
  248. }
  249. m_nature_value[index].is_select = false;
  250. CButtonUI* old_btn = static_cast<CButtonUI*>(pValueLayout->GetItemAt(index));
  251. ApplySelectStyle(old_btn, index, false);
  252. m_nSelectNum--;
  253. }
  254. }
  255. else
  256. {
  257. //点击的这个是非选中状态
  258. if (m_nLimitChoose == 1)
  259. {
  260. //如果最大限制就是1个,点击非选中状态的,就自动选中这个
  261. int num = 0;
  262. for (std::vector<FoodNatureValue>::iterator it = m_nature_value.begin(); it != m_nature_value.end(); it++)
  263. {
  264. if (it->is_select == true)
  265. {
  266. //把旧的选中的设为非选中
  267. m_nature_value[num].is_select = false;
  268. CButtonUI* old_btn = static_cast<CButtonUI*>(pValueLayout->GetItemAt(num));
  269. ApplySelectStyle(old_btn, num, false);
  270. }
  271. num++;
  272. }
  273. //再把刚点击的设为选中
  274. m_nature_value[index].is_select = true;
  275. CButtonUI* new_btn = static_cast<CButtonUI*>(pValueLayout->GetItemAt(index));
  276. ApplySelectStyle(new_btn, index, true);
  277. }
  278. else
  279. {
  280. if (m_nSelectNum >= m_nLimitChoose)
  281. {
  282. m_pNatureWnd->ShowToast(L"最多选择" + std::to_wstring(m_nLimitChoose) + L"个属性");
  283. return;
  284. }
  285. //否则进行选中
  286. m_nature_value[index].is_select = true;
  287. CButtonUI* new_btn = static_cast<CButtonUI*>(pValueLayout->GetItemAt(index));
  288. ApplySelectStyle(new_btn, index, true);
  289. m_nSelectNum++;
  290. }
  291. }
  292. }
  293. std::string CDiandanNatureItemUI::GetNatureName()
  294. {
  295. return m_nature_name;
  296. }
  297. int CDiandanNatureItemUI::GetNatureNameNum()
  298. {
  299. return m_nNatureNameNum;
  300. }
  301. std::vector<FoodNatureSelectValueInfo> CDiandanNatureItemUI::GetSelectedNatureInfo()
  302. {
  303. std::vector<FoodNatureSelectValueInfo> infoArray;
  304. int num = 0;
  305. for (std::vector<FoodNatureValue>::iterator it = m_nature_value.begin(); it != m_nature_value.end(); it++)
  306. {
  307. if (it->is_select == true)
  308. {
  309. FoodNatureSelectValueInfo newInfo;
  310. newInfo.nature_select_name = (*it).name;
  311. newInfo.nature_select_value = (*it).value;
  312. newInfo.nature_select_index = num;
  313. infoArray.push_back(newInfo);
  314. }
  315. num++;
  316. }
  317. return infoArray;
  318. }