soui 5.0.0.1
Soui5 Doc
 
Loading...
Searching...
No Matches
SouiLayout.cpp
1#include "souistd.h"
2#include "layout/SouiLayout.h"
3#include "helper/SplitString.h"
4#include <core/SWnd.h>
5#pragma warning(push)
6#pragma warning(disable : 4985) // disable the warning message during the include
7#include <math.h> // this is where I would normally get the warning message
8#pragma warning(pop)
9
10SNSBEGIN
11enum
12{
13 POS_INIT = 0x11000000, //坐标的初始化值
14 POS_WAIT = 0x12000000, //坐标的计算依赖于其它窗口的布局
15};
16
21
22BOOL SouiLayoutParam::IsMatchParent(ORIENTATION orientation) const
23{
24 switch (orientation)
25 {
26 case Horz:
27 return width.isMatchParent();
28 case Vert:
29 return height.isMatchParent();
30 case Any:
31 return IsMatchParent(Horz) || IsMatchParent(Vert);
32 case Both:
33 default:
34 return IsMatchParent(Horz) && IsMatchParent(Vert);
35 }
36}
37
38BOOL SouiLayoutParam::IsSpecifiedSize(ORIENTATION orientation) const
39{
40 switch (orientation)
41 {
42 case Horz:
43 return width.isSpecifiedSize();
44 case Vert:
45 return height.isSpecifiedSize();
46 case Any:
47 return IsSpecifiedSize(Horz) || IsSpecifiedSize(Vert);
48 case Both:
49 default:
50 return IsSpecifiedSize(Horz) && IsSpecifiedSize(Vert);
51 }
52}
53
54BOOL SouiLayoutParam::IsWrapContent(ORIENTATION orientation) const
55{
56 switch (orientation)
57 {
58 case Horz:
59 return width.isWrapContent() || (nCount == 0 && !width.isValid());
60 case Vert:
61 return height.isWrapContent() || (nCount == 0 && !height.isValid());
62 case Any:
63 return IsWrapContent(Horz) || IsWrapContent(Vert);
64 case Both:
65 default:
66 return IsWrapContent(Horz) && IsWrapContent(Vert);
67 }
68}
69
70SLayoutSize SouiLayoutParam::GetSpecifiedSize(ORIENTATION orientation) const
71{
72 if (orientation == Vert)
73 return height;
74 else
75 return width;
76}
77
78HRESULT SouiLayoutParam::OnAttrOffset(const SStringW &strValue, BOOL bLoading)
79{
80 float fx, fy;
81 if (2 != swscanf(strValue, L"%f,%f", &fx, &fy))
82 {
83 return E_FAIL;
84 }
85 fOffsetX = fx;
86 fOffsetY = fy;
87 return S_OK;
88}
89
90BOOL SouiLayoutParam::ParsePosition12(const SStringW &strPos1, const SStringW &strPos2)
91{
92 if (strPos1.IsEmpty() || strPos2.IsEmpty())
93 return FALSE;
94 POS_INFO pos1, pos2;
95 if (!StrPos2ItemPos(strPos1, pos1) || !StrPos2ItemPos(strPos2, pos2))
96 return FALSE;
97 if (pos1.pit == PIT_SIZE || pos2.pit == PIT_SIZE) //前面2个属性不能是size类型
98 return FALSE;
99 posLeft = pos1;
100 posTop = pos2;
101 nCount = 2;
102 return TRUE;
103}
104
105BOOL SouiLayoutParam::ParsePosition34(const SStringW &strPos3, const SStringW &strPos4)
106{
107 if (strPos3.IsEmpty() || strPos4.IsEmpty())
108 return FALSE;
109 POS_INFO pos3, pos4;
110 if (!StrPos2ItemPos(strPos3, pos3) || !StrPos2ItemPos(strPos4, pos4))
111 return FALSE;
112
113 posRight = pos3;
114 posBottom = pos4;
115 nCount = 4;
116 return TRUE;
117}
118
119BOOL SouiLayoutParam::StrPos2ItemPos(const SStringW &strPos, POS_INFO &pos)
120{
121 if (strPos.IsEmpty())
122 return FALSE;
123
124 if (strPos.Left(4) == L"sib.")
125 {
126 int nOffset = 0;
127 if (strPos.Mid(4, 5) == L"left@")
128 {
129 pos.pit = PIT_SIB_LEFT;
130 nOffset = 5;
131 }
132 else if (strPos.Mid(4, 6) == L"right@")
133 {
134 pos.pit = PIT_SIB_RIGHT;
135 nOffset = 6;
136 }
137 else if (strPos.Mid(4, 4) == L"top@")
138 {
139 pos.pit = PIT_SIB_TOP;
140 nOffset = 4;
141 }
142 else if (strPos.Mid(4, 7) == L"bottom@")
143 {
144 pos.pit = PIT_SIB_BOTTOM;
145 nOffset = 7;
146 }
147 else
148 {
149 return FALSE;
150 }
151 SStringW strValue = strPos.Mid(4 + nOffset);
152 SStringWList values;
153 if (SplitString(strValue, L':', values) != 2)
154 return FALSE;
155 pos.nRefID = _wtoi(values[0]);
156 if (pos.nRefID == 0)
157 return FALSE;
158 pos.nPos = GETLAYOUTSIZE(values[1]);
159
160 if (pos.nPos.fSize < 0)
161 {
162 pos.nPos.fSize *= -1;
163 pos.bMinus = true;
164 }
165 else
166 {
167 pos.bMinus = false;
168 }
169 }
170 else
171 {
172 LPCWSTR pszPos = strPos;
173 switch (pszPos[0])
174 {
175 case POSFLAG_REFCENTER:
176 pos.pit = PIT_CENTER, pszPos++;
177 break;
178 case POSFLAG_PERCENT:
179 pos.pit = PIT_PERCENT, pszPos++;
180 break;
181 case POSFLAG_REFPREV_NEAR:
182 pos.pit = PIT_PREV_NEAR, pszPos++;
183 break;
184 case POSFLAG_REFNEXT_NEAR:
185 pos.pit = PIT_NEXT_NEAR, pszPos++;
186 break;
187 case POSFLAG_REFPREV_FAR:
188 pos.pit = PIT_PREV_FAR, pszPos++;
189 break;
190 case POSFLAG_REFNEXT_FAR:
191 pos.pit = PIT_NEXT_FAR, pszPos++;
192 break;
193 case POSFLAG_SIZE:
194 pos.pit = PIT_SIZE, pszPos++;
195 break;
196 default:
197 pos.pit = PIT_NORMAL;
198 break;
199 }
200
201 pos.nRefID = -1; // not ref sibling using id
202 if (pszPos[0] == L'-')
203 {
204 pos.bMinus = true;
205 pszPos++;
206 }
207 else
208 {
209 pos.bMinus = false;
210 }
211 pos.nPos = GETLAYOUTSIZE(pszPos);
212 }
213
214 return TRUE;
215}
216
217HRESULT SouiLayoutParam::OnAttrPos(const SStringW &strValue, BOOL bLoading)
218{
219 SStringWList strLst;
220 SplitString(strValue, L',', strLst);
221 if (strLst.GetCount() != 2 && strLst.GetCount() != 4)
222 {
223 SSLOGW() << "Parse pos attribute failed, strPos=" << strValue;
224 return E_INVALIDARG;
225 }
226 //增加pos属性中的空格兼容。
227 for (size_t i = 0; i < strLst.GetCount(); i++)
228 {
229 strLst.GetAt(i).TrimBlank();
230 }
231 BOOL bRet = TRUE;
232
233 bRet = ParsePosition12(strLst[0], strLst[1]);
234 if (strLst.GetCount() == 4)
235 {
236 bRet = ParsePosition34(strLst[2], strLst[3]);
237 }
238 if (bRet && nCount == 4)
239 { //检测X,Y方向上是否为充满父窗口
240 if ((posLeft.pit == PIT_NORMAL && posLeft.nPos.isZero() && (!posTop.bMinus)) && (posRight.pit == PIT_NORMAL && posRight.nPos.isZero() && posBottom.bMinus))
241 {
242 width.setMatchParent();
243 }
244 else if (posRight.pit == PIT_SIZE)
245 {
246 if (posRight.bMinus)
247 width.setWrapContent();
248 else
249 width = posRight.nPos;
250 }
251 else
252 {
253 width.setInvalid();
254 }
255
256 if ((posTop.pit == PIT_NORMAL && posTop.nPos.isZero() && (!posTop.bMinus)) && (posBottom.pit == PIT_NORMAL && posBottom.nPos.isZero() && posBottom.bMinus))
257 {
258 height.setMatchParent();
259 }
260 else if (posBottom.pit == PIT_SIZE)
261 {
262 if (posBottom.bMinus)
263 height.setWrapContent();
264 else
265 height = posBottom.nPos;
266 }
267 else
268 {
269 height.setInvalid();
270 }
271 }
272 else
273 {
274 if (!width.isValid())
275 SetWrapContent(Horz);
276 if (!height.isValid())
277 SetWrapContent(Vert);
278 }
279
280 return S_OK;
281}
282
283HRESULT SouiLayoutParam::OnAttrSize(const SStringW &strValue, BOOL bLoading)
284{
285 SStringWList szStr;
286 if (2 != SplitString(strValue, L',', szStr))
287 return E_FAIL;
288
289 OnAttrWidth(szStr[0], bLoading);
290 OnAttrHeight(szStr[1], bLoading);
291 return S_OK;
292}
293
294HRESULT SouiLayoutParam::OnAttrHeight(const SStringW &strValue, BOOL bLoading)
295{
296 if (strValue.CompareNoCase(L"matchParent") == 0 || strValue.CompareNoCase(L"full") == 0)
297 height.setMatchParent();
298 else if (strValue.CompareNoCase(L"wrapContent") == 0)
299 height.setWrapContent();
300 else
301 height = GETLAYOUTSIZE(strValue);
302 return S_OK;
303}
304
305HRESULT SouiLayoutParam::OnAttrWidth(const SStringW &strValue, BOOL bLoading)
306{
307 if (strValue.CompareNoCase(L"matchParent") == 0 || strValue.CompareNoCase(L"full") == 0)
308 width.setMatchParent();
309 else if (strValue.CompareNoCase(L"wrapContent") == 0)
310 width.setWrapContent();
311 else
312 width = GETLAYOUTSIZE(strValue);
313 return S_OK;
314}
315
316bool SouiLayoutParam::IsOffsetRequired(ORIENTATION orientation) const
317{
318 return fabs(orientation == Vert ? fOffsetY : fOffsetX) > 0.00000001f;
319}
320
321int GetPosExtra(const POS_INFO &pos, int nScale)
322{
323 return pos.bMinus ? pos.nPos.toPixelSize(nScale) : 0;
324}
325
326int SouiLayoutParam::GetExtraSize(ORIENTATION orientation, int nScale) const
327{
328 if (nCount != 4)
329 return 0;
330 if (orientation == Horz)
331 return GetPosExtra(posRight, nScale);
332 else
333 return GetPosExtra(posBottom, nScale);
334}
335
337{
338 nCount = 0;
339 fOffsetX = fOffsetY = 0.0f;
340
341 width.setWrapContent();
342 height.setWrapContent();
343}
344
345void SouiLayoutParam::SetMatchParent(ORIENTATION orientation)
346{
347 switch (orientation)
348 {
349 case Horz:
350 width.setMatchParent();
351 break;
352 case Vert:
353 height.setMatchParent();
354 break;
355 case Both:
356 width.setMatchParent();
357 height.setMatchParent();
358 break;
359 }
360}
361
362void SouiLayoutParam::SetWrapContent(ORIENTATION orientation)
363{
364 switch (orientation)
365 {
366 case Horz:
367 width.setWrapContent();
368 break;
369 case Vert:
370 height.setWrapContent();
371 break;
372 case Both:
373 width.setWrapContent();
374 height.setWrapContent();
375 break;
376 }
377}
378
379void SouiLayoutParam::SetSpecifiedSize(ORIENTATION orientation, const SLayoutSize &layoutSize)
380{
381 switch (orientation)
382 {
383 case Horz:
384 width = layoutSize;
385 break;
386 case Vert:
387 height = layoutSize;
388 break;
389 case Both:
390 width = height = layoutSize;
391 break;
392 }
393}
394
396{
397 return (SouiLayoutParamStruct *)this;
398}
399
400ILayoutParam *SouiLayoutParam::Clone() const
401{
402 SouiLayoutParam *pRet = new SouiLayoutParam();
403 memcpy(pRet->GetRawData(), (SouiLayoutParamStruct *)this, sizeof(SouiLayoutParamStruct));
404 return pRet;
405}
406
407//////////////////////////////////////////////////////////////////////////
408
412
416
417BOOL SouiLayout::IsParamAcceptable(const ILayoutParam *pLayoutParam) const
418{
419 return !!pLayoutParam->IsClass(SouiLayoutParam::GetClassName());
420}
421
422ILayoutParam *SouiLayout::CreateLayoutParam() const
423{
424 return new SouiLayoutParam();
425}
426
427BOOL SouiLayout::IsWaitingPos(int nPos) const
428{
429 return nPos == POS_INIT || nPos == POS_WAIT;
430}
431
432int SouiLayout::PositionItem2Value(SList<WndPos> *pLstChilds, SPOSITION position, const POS_INFO &pos, int nMax, BOOL bX, int nScale) const
433{
434 int nRet = POS_WAIT;
435
436 switch (pos.pit)
437 {
438 case PIT_CENTER: //参考中心
439 if (nMax != SIZE_WRAP_CONTENT)
440 nRet = pos.nPos.toPixelSize(nScale) * (pos.bMinus ? -1 : 1) + nMax / 2;
441 break;
442 case PIT_NORMAL:
443 if (pos.bMinus)
444 { //参考右边或者下边
445 if (nMax != SIZE_WRAP_CONTENT)
446 nRet = nMax - pos.nPos.toPixelSize(nScale);
447 }
448 else
449 {
450 nRet = pos.nPos.toPixelSize(nScale);
451 }
452 break;
453 case PIT_PERCENT:
454 if (nMax != SIZE_WRAP_CONTENT)
455 {
456 float fPercent = pos.nPos.fSize;
457 if (fPercent < 0.0f)
458 fPercent = 0.0f;
459 if (fPercent > 100.0f)
460 fPercent = 100.0f;
461 if (pos.bMinus)
462 nRet = (int)((100.0f - fPercent) * nMax / 100);
463 else
464 nRet = (int)(fPercent * nMax / 100);
465 }
466 break;
467 case PIT_PREV_NEAR:
468 case PIT_PREV_FAR:
469 {
470 SPOSITION positionPrev = pLstChilds->Prev(position);
471 int nRef = POS_WAIT;
472 if (positionPrev)
473 {
474 WndPos wndPos = pLstChilds->GetAt(positionPrev);
475 if (bX)
476 {
477 if (!wndPos.bWaitOffsetX)
478 nRef = (pos.pit == PIT_PREV_NEAR) ? wndPos.rc.right : wndPos.rc.left;
479 }
480 else
481 {
482 if (!wndPos.bWaitOffsetY)
483 nRef = (pos.pit == PIT_PREV_NEAR) ? wndPos.rc.bottom : wndPos.rc.top;
484 }
485 }
486 else
487 {
488 nRef = 0;
489 }
490 if (!IsWaitingPos(nRef))
491 nRet = nRef + pos.nPos.toPixelSize(nScale) * (pos.bMinus ? -1 : 1);
492 }
493 break;
494 case PIT_NEXT_NEAR:
495 case PIT_NEXT_FAR:
496 {
497 SPOSITION positionNext = pLstChilds->Next(position);
498 int nRef = nMax;
499 if (positionNext)
500 {
501 nRef = POS_WAIT;
502 WndPos wndPos = pLstChilds->GetAt(positionNext);
503 if (bX)
504 {
505 if (!wndPos.bWaitOffsetX)
506 nRef = (pos.pit == PIT_NEXT_NEAR) ? wndPos.rc.left : wndPos.rc.right;
507 }
508 else
509 {
510 if (!wndPos.bWaitOffsetY)
511 nRef = (pos.pit == PIT_NEXT_NEAR) ? wndPos.rc.top : wndPos.rc.bottom;
512 }
513 }
514 if (!IsWaitingPos(nRef))
515 nRet = nRef + pos.nPos.toPixelSize(nScale) * (pos.bMinus ? -1 : 1);
516 }
517 break;
518 case PIT_SIB_LEFT: // PIT_SIB_LEFT == PIT_SIB_TOP
519 case PIT_SIB_RIGHT: // PIT_SIB_RIGHT == PIT_SIB_BOTTOM
520 {
521 WndPos wndPos = pLstChilds->GetAt(position);
522 SASSERT(pos.nRefID > 0);
523
524 WndPos wndPosRef = { 0 };
525 SPOSITION posTmp = pLstChilds->GetHeadPosition();
526 while (posTmp)
527 {
528 WndPos wp = pLstChilds->GetNext(posTmp);
529 if (wp.pWnd->GetID() == pos.nRefID)
530 {
531 wndPosRef = wp;
532 break;
533 }
534 }
535 if (!wndPosRef.pWnd)
536 { //没有找到时,使用父窗口信息
537 wndPosRef.rc = CRect(0, 0, nMax, nMax);
538 wndPosRef.bWaitOffsetX = wndPosRef.bWaitOffsetY = false;
539 }
540 CRect rcRef = wndPosRef.rc;
541
542 if (bX)
543 {
544 if (!wndPosRef.bWaitOffsetX)
545 {
546 LONG refPos = (pos.pit == PIT_SIB_LEFT) ? rcRef.left : rcRef.right;
547 if (IsWaitingPos(refPos))
548 nRet = POS_WAIT;
549 else
550 nRet = refPos + pos.nPos.toPixelSize(nScale) * (pos.bMinus ? -1 : 1);
551 }
552 }
553 else
554 {
555 if (!wndPosRef.bWaitOffsetY)
556 {
557 LONG refPos = (pos.pit == PIT_SIB_TOP) ? rcRef.top : rcRef.bottom; // PIT_SIB_TOP == PIT_SIB_LEFT
558 if (IsWaitingPos(refPos))
559 nRet = POS_WAIT;
560 else
561 nRet = refPos + pos.nPos.toPixelSize(nScale) * (pos.bMinus ? -1 : 1);
562 }
563 }
564 }
565 break;
566 }
567
568 return nRet;
569}
570
571SIZE SouiLayout::MeasureChildren(const IWindow *pParent, int nWidth, int nHeight) const
572{
573 SList<WndPos> lstWndPos;
574
575 const IWindow *pChild = pParent->GetNextLayoutIChild(NULL);
576 while (pChild)
577 {
578 if (!pChild->IsFloat() && (pChild->IsVisible(FALSE) || pChild->IsDisplay()))
579 { //不显示且不占位的窗口不参与计算
580 WndPos wndPos;
581 wndPos.pWnd = (SWindow *)pChild;
582 wndPos.rc = CRect(POS_INIT, POS_INIT, POS_INIT, POS_INIT);
583 const SouiLayoutParam *pParam = (const SouiLayoutParam *)pChild->GetLayoutParam();
584 wndPos.bWaitOffsetX = pParam->IsOffsetRequired(Horz);
585 wndPos.bWaitOffsetY = pParam->IsOffsetRequired(Vert);
586 lstWndPos.AddTail(wndPos);
587 }
588 pChild = pParent->GetNextLayoutIChild(pChild);
589 }
590
591 //计算子窗口位置
592 CalcPositionEx(&lstWndPos, nWidth, nHeight);
593
594 //计算子窗口范围
595 int nMaxX = 0, nMaxY = 0;
596 SPOSITION pos = lstWndPos.GetHeadPosition();
597 while (pos)
598 {
599 WndPos wndPos = lstWndPos.GetNext(pos);
600 const SouiLayoutParam *pParam = (const SouiLayoutParam *)wndPos.pWnd->GetLayoutParam();
601 int nScale = wndPos.pWnd->GetScale();
602 if (!IsWaitingPos(wndPos.rc.right))
603 {
604 nMaxX = smax(nMaxX, (int)(wndPos.rc.right + pParam->GetExtraSize(Horz, nScale)));
605 }
606 if (!IsWaitingPos(wndPos.rc.bottom))
607 {
608 nMaxY = smax(nMaxY, (int)(wndPos.rc.bottom + pParam->GetExtraSize(Vert, nScale)));
609 }
610 }
611
612 if (!IsWaitingPos(nWidth))
613 nWidth = nMaxX;
614 if (!IsWaitingPos(nHeight))
615 nHeight = nMaxY;
616 return CSize(nWidth, nHeight);
617}
618
619/*
620计算子窗口容器大小逻辑:
6211:引用父窗口左上角的窗口称之为I类确定性窗口。
6222:引用I类窗口的窗口称为II类确定性窗口。
6233:左边引用父窗口左上角或者I,II类确定性窗口,右边引用父窗口右下角的窗口为I不确定性窗口,这类窗口自动转换成自适应大小窗口。
6244:左右都引用父窗口右下角的窗口为II类不确定窗口,这类窗口不影响父窗口大小。
6255:引用I,II类不确定大小窗口的窗口同样不影响父窗口大小。
626
627只要一个控件左边位置能确定,控件的右边也可以保证可以确定。
628如果左边位置不能确定,则控件大小不影响父窗口大小。
629*/
630void SouiLayout::CalcPositionEx(SList<WndPos> *pListChildren, int nWidth, int nHeight) const
631{
632 CalcPostion(pListChildren, nWidth, nHeight);
633
634 //将参考父窗口右边或者底边的子窗口设置为wrap_content并计算出大小
635
636 int nResolved = 0;
637 for (SPOSITION pos = pListChildren->GetHeadPosition(); pos; pListChildren->GetNext(pos))
638 {
639 WndPos &wndPos = pListChildren->GetAt(pos);
640 const SouiLayoutParam *pLayoutParam = (const SouiLayoutParam *)wndPos.pWnd->GetLayoutParam();
641 if (!IsWaitingPos(wndPos.rc.left) && !IsWaitingPos(wndPos.rc.top) && (IsWaitingPos(wndPos.rc.right) && IsWaitingPos(nWidth) || IsWaitingPos(wndPos.rc.bottom) && IsWaitingPos(nHeight)))
642 {
643 int nWid = IsWaitingPos(wndPos.rc.right) ? nWidth : (wndPos.rc.right - wndPos.rc.left);
644 int nHei = IsWaitingPos(wndPos.rc.bottom) ? nHeight : (wndPos.rc.bottom - wndPos.rc.top);
645 CSize szWnd;
646 wndPos.pWnd->GetDesiredSize(&szWnd, nWid, nHei);
647 if (pLayoutParam->IsWrapContent(Horz))
648 {
649 wndPos.rc.right = wndPos.rc.left + szWnd.cx;
650 if (wndPos.bWaitOffsetX)
651 {
652 wndPos.rc.OffsetRect((int)(wndPos.rc.Width() * pLayoutParam->fOffsetX), 0);
653 wndPos.bWaitOffsetX = false;
654 }
655 nResolved++;
656 }
657 if (pLayoutParam->IsWrapContent(Vert))
658 {
659 wndPos.rc.bottom = wndPos.rc.top + szWnd.cy;
660 if (wndPos.bWaitOffsetY)
661 {
662 wndPos.rc.OffsetRect(0, (int)(wndPos.rc.Height() * pLayoutParam->fOffsetY));
663 wndPos.bWaitOffsetY = false;
664 }
665 nResolved++;
666 }
667 }
668 }
669}
670
671static const POS_INFO posRefLeft = { PIT_PREV_NEAR, -1, 1 };
672static const POS_INFO posRefTop = { PIT_PREV_FAR, -1, 1 };
673
674int SouiLayout::CalcPostion(SList<WndPos> *pListChildren, int nWidth, int nHeight) const
675{
676 int nResolvedAll = 0;
677
678 int nResolvedStep1 = 0;
679 int nResolvedStep2 = 0;
680 do
681 {
682 nResolvedStep1 = 0;
683 nResolvedStep2 = 0;
684
685 // step 1:计算出所有不需要计算窗口大小就可以确定的坐标
686 int nResolved = 0;
687 do
688 {
689 nResolved = 0;
690 for (SPOSITION pos = pListChildren->GetHeadPosition(); pos; pListChildren->GetNext(pos))
691 {
692 WndPos &wndPos = pListChildren->GetAt(pos);
693 const SouiLayoutParam *pLayoutParam = (const SouiLayoutParam *)wndPos.pWnd->GetLayoutParam();
694 int nScale = wndPos.pWnd->GetScale();
695 if (IsWaitingPos(wndPos.rc.left))
696 {
697 const POS_INFO &posRef = pLayoutParam->nCount >= 2 ? pLayoutParam->posLeft : posRefLeft;
698 wndPos.rc.left = PositionItem2Value(pListChildren, pos, posRef, nWidth, TRUE, nScale);
699 if (wndPos.rc.left != POS_WAIT)
700 nResolved++;
701 }
702 if (IsWaitingPos(wndPos.rc.top))
703 {
704 const POS_INFO &posRef = pLayoutParam->nCount >= 2 ? pLayoutParam->posTop : posRefTop;
705 wndPos.rc.top = PositionItem2Value(pListChildren, pos, posRef, nHeight, FALSE, nScale);
706 if (wndPos.rc.top != POS_WAIT)
707 nResolved++;
708 }
709 if (IsWaitingPos(wndPos.rc.right))
710 {
711 if (pLayoutParam->IsMatchParent(Horz))
712 {
713 wndPos.rc.right = nWidth;
714 }
715 else if (pLayoutParam->IsSpecifiedSize(Horz))
716 {
717 if (!IsWaitingPos(wndPos.rc.left))
718 {
719 wndPos.rc.right = wndPos.rc.left + pLayoutParam->GetSpecifiedSize(Horz).toPixelSize(nScale);
720 nResolved++;
721 }
722 }
723 else if (!pLayoutParam->IsWrapContent(Horz) && pLayoutParam->nCount == 4)
724 {
725 wndPos.rc.right = PositionItem2Value(pListChildren, pos, pLayoutParam->posRight, nWidth, TRUE, nScale);
726 if (wndPos.rc.right != POS_WAIT)
727 nResolved++;
728 }
729 }
730 if (IsWaitingPos(wndPos.rc.bottom))
731 {
732 if (pLayoutParam->IsMatchParent(Vert))
733 {
734 wndPos.rc.bottom = nHeight;
735 }
736 else if (pLayoutParam->IsSpecifiedSize(Vert))
737 {
738 if (!IsWaitingPos(wndPos.rc.top))
739 {
740 wndPos.rc.bottom = wndPos.rc.top + pLayoutParam->GetSpecifiedSize(Vert).toPixelSize(nScale);
741 nResolved++;
742 }
743 }
744 else if (!pLayoutParam->IsWrapContent(Vert) && pLayoutParam->nCount == 4)
745 {
746 wndPos.rc.bottom = PositionItem2Value(pListChildren, pos, pLayoutParam->posBottom, nHeight, FALSE, nScale);
747 if (wndPos.rc.bottom != POS_WAIT)
748 nResolved++;
749 }
750 }
751 }
752
753 nResolvedStep1 += nResolved;
754 } while (nResolved);
755
756 if (nResolvedStep1 > 0)
757 {
758 int nResolved = 0;
759 // step 2:计算出自适应大小窗口的Size,对于可以确定的窗口完成offset操作
760 do
761 {
762 nResolved = 0;
763 for (SPOSITION pos = pListChildren->GetHeadPosition(); pos; pListChildren->GetNext(pos))
764 {
765 WndPos &wndPos = pListChildren->GetAt(pos);
766 const SouiLayoutParam *pLayoutParam = (const SouiLayoutParam *)wndPos.pWnd->GetLayoutParam();
767 if (IsWaitingPos(wndPos.rc.left) || IsWaitingPos(wndPos.rc.top))
768 continue; //至少确定了一个点后才开始计算
769
770 if ((IsWaitingPos(wndPos.rc.right) && pLayoutParam->IsWrapContent(Horz)) || (IsWaitingPos(wndPos.rc.bottom) && pLayoutParam->IsWrapContent(Vert)))
771 { //
772 int nWid = IsWaitingPos(wndPos.rc.right) ? nWidth : (wndPos.rc.right - wndPos.rc.left);
773 int nHei = IsWaitingPos(wndPos.rc.bottom) ? nHeight : (wndPos.rc.bottom - wndPos.rc.top);
774 CSize szWnd;
775 wndPos.pWnd->GetDesiredSize(&szWnd, nWid, nHei);
776 if (pLayoutParam->IsWrapContent(Horz))
777 {
778 wndPos.rc.right = wndPos.rc.left + szWnd.cx;
779 nResolved++;
780 }
781 if (pLayoutParam->IsWrapContent(Vert))
782 {
783 wndPos.rc.bottom = wndPos.rc.top + szWnd.cy;
784 nResolved++;
785 }
786 }
787 if (!IsWaitingPos(wndPos.rc.right) && wndPos.bWaitOffsetX)
788 {
789 wndPos.rc.OffsetRect((int)(wndPos.rc.Width() * pLayoutParam->fOffsetX), 0);
790 wndPos.bWaitOffsetX = false;
791 }
792 if (!IsWaitingPos(wndPos.rc.bottom) && wndPos.bWaitOffsetY)
793 {
794 wndPos.rc.OffsetRect(0, (int)(wndPos.rc.Height() * pLayoutParam->fOffsetY));
795 wndPos.bWaitOffsetY = false;
796 }
797 }
798 nResolvedStep2 += nResolved;
799 } while (nResolved);
800 } // end if(nResolvedStep1>0)
801
802 nResolvedAll += nResolvedStep1 + nResolvedStep2;
803 } while (nResolvedStep2 || nResolvedStep1);
804
805 return nResolvedAll;
806}
807
808void SouiLayout::LayoutChildren(IWindow *pParent)
809{
810 SList<WndPos> lstWndPos;
811
812 IWindow *pChild = pParent->GetNextLayoutIChild(NULL);
813 while (pChild)
814 {
815 WndPos wndPos;
816 wndPos.pWnd = (SWindow *)pChild;
817 wndPos.rc = CRect(POS_INIT, POS_INIT, POS_INIT, POS_INIT);
818 const SouiLayoutParam *pParam = (const SouiLayoutParam *)pChild->GetLayoutParam();
819 wndPos.bWaitOffsetX = pParam->IsOffsetRequired(Horz);
820 wndPos.bWaitOffsetY = pParam->IsOffsetRequired(Vert);
821 lstWndPos.AddTail(wndPos);
822
823 pChild = pParent->GetNextLayoutIChild(pChild);
824 }
825
826 if (lstWndPos.IsEmpty())
827 return;
828
829 CRect rcParent;
830 pParent->GetChildrenLayoutRect(&rcParent);
831 //计算子窗口位置
832 CalcPostion(&lstWndPos, rcParent.Width(), rcParent.Height());
833
834 //偏移窗口坐标
835 SPOSITION pos = lstWndPos.GetHeadPosition();
836 while (pos)
837 {
838 WndPos wp = lstWndPos.GetNext(pos);
839 wp.rc.OffsetRect(rcParent.TopLeft());
840 wp.pWnd->OnRelayout(wp.rc);
841 }
842}
843
844SNSEND
SOUI基础DUI窗口模块
布局大小类
Definition SLayoutSize.h:10
int toPixelSize(int scale) const
将大小转换为像素值
int GetID() SCONST OVERRIDE
Retrieves the object's ID.
Definition Sobject.hpp:134
static LPCWSTR GetClassName()
Definition Sobject.hpp:41
A class representing an ASCII string.
Definition sstringw.h:96
int CompareNoCase(const wchar_t *psz) SCONST
Compares the string with another string, ignoring case.
Definition sstringw.cpp:929
BOOL IsEmpty() SCONST
Checks if the string is empty.
SStringW Mid(int nFirst) const
Extracts a substring from the string.
Definition sstringw.cpp:924
SStringW Left(int nCount) const
Extracts the leftmost part of the string.
Definition sstringw.cpp:879
Base class for SOUI DUI windows.
Definition SWnd.h:286
void GetDesiredSize(SIZE *psz, int nParentWid, int nParentHei) OVERRIDE
Retrieves the desired size of the window.
Definition Swnd.cpp:1839
int GetScale() SCONST OVERRIDE
Retrieves the scale factor of the window.
Definition Swnd.cpp:3266
virtual BOOL OnRelayout(const CRect &rcWnd)
Handles window position changes during layout updates.
Definition Swnd.cpp:1587
ILayoutParam * GetLayoutParam() SCONST OVERRIDE
Retrieves the layout parameter object associated with the window.
Definition SWnd.h:405
void CalcPositionEx(SList< WndPos > *pListChildren, int nWidth, int nHeight) const
计算扩展位置
BOOL IsParamAcceptable(const ILayoutParam *pLayoutParam) SCONST OVERRIDE
检查布局参数是否可接受
int PositionItem2Value(SList< WndPos > *pLstChilds, SPOSITION position, const POS_INFO &pos, int nMax, BOOL bX, int nScale) const
将位置项转换为值
SIZE MeasureChildren(const IWindow *pParent, int nWidth, int nHeight) SCONST OVERRIDE
测量子窗口大小
SouiLayout(void)
构造函数
~SouiLayout(void)
析构函数
int CalcPostion(SList< WndPos > *pListChildren, int nWidth, int nHeight) const
计算位置
ILayoutParam * CreateLayoutParam() SCONST OVERRIDE
创建布局参数对象
BOOL IsWaitingPos(int nPos) const
检查位置是否等待
void LayoutChildren(IWindow *pParent) OVERRIDE
布局子窗口
Soui布局参数类
Definition SouiLayout.h:16
ILayoutParam * Clone() SCONST OVERRIDE
克隆布局参数
BOOL IsSpecifiedSize(ORIENTATION orientation) SCONST OVERRIDE
检查是否指定大小
SouiLayoutParam()
构造函数
BOOL ParsePosition12(const SStringW &pos1, const SStringW &pos2)
解析在pos中定义的前两个位置
void SetSpecifiedSize(ORIENTATION orientation, const SLayoutSize &layoutSize) OVERRIDE
设置指定大小
HRESULT OnAttrPos(const SStringW &strValue, BOOL bLoading)
处理位置属性
HRESULT OnAttrSize(const SStringW &strValue, BOOL bLoading)
处理大小属性
BOOL StrPos2ItemPos(const SStringW &strPos, POS_INFO &posItem)
将字符串描述的坐标转换成POSITION_ITEM
HRESULT OnAttrWidth(const SStringW &strValue, BOOL bLoading)
处理宽度属性
void Clear() OVERRIDE
清除布局参数
void * GetRawData() OVERRIDE
获取原始数据指针
SLayoutSize GetSpecifiedSize(ORIENTATION orientation) SCONST OVERRIDE
获取指定大小
int GetExtraSize(ORIENTATION orientation, int nScale) const
获取额外大小
HRESULT OnAttrHeight(const SStringW &strValue, BOOL bLoading)
处理高度属性
BOOL IsWrapContent(ORIENTATION orientation) SCONST OVERRIDE
检查是否包裹内容大小
void SetMatchParent(ORIENTATION orientation) OVERRIDE
设置匹配父容器大小
bool IsOffsetRequired(ORIENTATION orientation) const
检查是否需要偏移
BOOL ParsePosition34(const SStringW &pos3, const SStringW &pos4)
解析在pos中定义的后两个位置
HRESULT OnAttrOffset(const SStringW &strValue, BOOL bLoading)
处理偏移属性
void SetWrapContent(ORIENTATION orientation) OVERRIDE
设置包裹内容大小
BOOL IsMatchParent(ORIENTATION orientation) SCONST OVERRIDE
检查是否匹配父容器大小
窗口位置结构体
Definition SouiLayout.h:236