From 5628d940bbf747cd7857a2f94674a153d5cdaa3f Mon Sep 17 00:00:00 2001 From: ppescher Date: Thu, 11 Jul 2002 17:47:46 +0000 Subject: [PATCH] many improvements for anti-flickering changes in internal layout implementation changes in clipping and refresh functions --- ResizableLib/ResizableLayout.cpp | 188 ++++++++++++++++++------------- ResizableLib/ResizableLayout.h | 62 +++++----- 2 files changed, 140 insertions(+), 110 deletions(-) diff --git a/ResizableLib/ResizableLayout.cpp b/ResizableLib/ResizableLayout.cpp index 9dd7169..c03104b 100644 --- a/ResizableLib/ResizableLayout.cpp +++ b/ResizableLib/ResizableLayout.cpp @@ -41,28 +41,19 @@ void CResizableLayout::AddAnchor(HWND hWnd, CSize sizeTypeTL, CSize sizeTypeBR) ASSERT(sizeTypeTL != NOANCHOR); // get control's window class - CString st; - GetClassName(hWnd, st.GetBufferSetLength(MAX_PATH), MAX_PATH); - st.ReleaseBuffer(); + CString sClassName; + GetClassName(hWnd, sClassName.GetBufferSetLength(MAX_PATH), MAX_PATH); + sClassName.ReleaseBuffer(); // add the style 'clipsiblings' to a GroupBox // to avoid unnecessary repainting of controls inside - if (st == "Button") + if (sClassName == "Button") { DWORD style = GetWindowLong(hWnd, GWL_STYLE); if ((style & BS_TYPEMASK) == BS_GROUPBOX) SetWindowLong(hWnd, GWL_STYLE, style | WS_CLIPSIBLINGS); } - // window classes that don't redraw client area correctly - // when the hor scroll pos changes due to a resizing - BOOL bHScroll = FALSE; - if (st == "ListBox") - bHScroll = TRUE; - - // window classes that need refresh when resized - BOOL bRefresh = NeedsRefresh(hWnd); - // get parent window's rect CRect rectParent; GetTotalClientRect(&rectParent); @@ -87,10 +78,12 @@ void CResizableLayout::AddAnchor(HWND hWnd, CSize sizeTypeTL, CSize sizeTypeBR) sizeMarginBR.cx = rectChild.right - rectParent.Width() * sizeTypeBR.cx / 100; sizeMarginBR.cy = rectChild.bottom - rectParent.Height() * sizeTypeBR.cy / 100; - // add to the map + // add to the list and the map LayoutInfo layout(hWnd, sizeTypeTL, sizeMarginTL, - sizeTypeBR, sizeMarginBR, bHScroll, bRefresh); - m_mapLayout.SetAt(hWnd, layout); + sizeTypeBR, sizeMarginBR, sClassName); + + POSITION pos = m_listLayout.AddTail(layout); + m_mapLayout.SetAt(hWnd, pos); } void CResizableLayout::AddAnchorCallback(UINT nCallbackID) @@ -105,7 +98,7 @@ void CResizableLayout::AddAnchorCallback(UINT nCallbackID) m_listLayoutCB.AddTail(layout); } -BOOL CResizableLayout::ArrangeLayoutCallback(LayoutInfo& /*layout*/) +BOOL CResizableLayout::ArrangeLayoutCallback(CResizableLayout::LayoutInfo& /*layout*/) { ASSERT(FALSE); // must be overridden, if callback is used @@ -120,21 +113,20 @@ void CResizableLayout::ArrangeLayout() LayoutInfo layout; CRect rectParent, rectChild; GetTotalClientRect(&rectParent); // get parent window's rect - int count = m_mapLayout.GetCount(); + int count = m_listLayout.GetCount(); int countCB = m_listLayoutCB.GetCount(); // reposition child windows HDWP hdwp = ::BeginDeferWindowPos(count); - HWND hWnd; - POSITION pos = m_mapLayout.GetStartPosition(); + POSITION pos = m_listLayout.GetHeadPosition(); while (pos != NULL) { // get layout info - m_mapLayout.GetNextAssoc(pos, hWnd, layout); + layout = m_listLayout.GetNext(pos); // calculate new child's position, size and flags for SetWindowPos - CalcNewChildPosition(rectParent, layout, rectChild, uFlags); + CalcNewChildPosition(layout, rectParent, rectChild, uFlags); // only if size or position changed if ((uFlags & (SWP_NOMOVE|SWP_NOSIZE)) != (SWP_NOMOVE|SWP_NOSIZE)) @@ -161,7 +153,7 @@ void CResizableLayout::ArrangeLayout() continue; // calculate new child's position, size and flags for SetWindowPos - CalcNewChildPosition(rectParent, layout, rectChild, uFlags); + CalcNewChildPosition(layout, rectParent, rectChild, uFlags); // only if size or position changed if ((uFlags & (SWP_NOMOVE|SWP_NOSIZE)) != (SWP_NOMOVE|SWP_NOSIZE)) @@ -173,17 +165,18 @@ void CResizableLayout::ArrangeLayout() ::EndDeferWindowPos(hdwp); } -void CResizableLayout::ClipChildWindow(HWND hWnd, CRgn* pRegion) +void CResizableLayout::ClipChildWindow(const CResizableLayout::LayoutInfo& layout, + CRgn* pRegion) { // obtain window position CRect rect; - ::GetWindowRect(hWnd, &rect); + ::GetWindowRect(layout.hWnd, &rect); ::MapWindowPoints(NULL, GetResizableWnd()->m_hWnd, (LPPOINT)&rect, 2); // use window region if any CRgn rgn; rgn.CreateRectRgn(0,0,0,0); - switch (::GetWindowRgn(hWnd, rgn)) + switch (::GetWindowRgn(layout.hWnd, rgn)) { case COMPLEXREGION: case SIMPLEREGION: @@ -195,7 +188,7 @@ void CResizableLayout::ClipChildWindow(HWND hWnd, CRgn* pRegion) } // go clipping? - if (LikesClipping(hWnd)) + if (LikesClipping(layout)) pRegion->CombineRgn(pRegion, &rgn, RGN_DIFF); else pRegion->CombineRgn(pRegion, &rgn, RGN_OR); @@ -221,15 +214,14 @@ void CResizableLayout::GetClippingRegion(CRgn* pRegion) // clip only anchored controls LayoutInfo layout; - HWND hWnd; - POSITION pos = m_mapLayout.GetStartPosition(); + POSITION pos = m_listLayout.GetHeadPosition(); while (pos != NULL) { // get layout info - m_mapLayout.GetNextAssoc(pos, hWnd, layout); + layout = m_listLayout.GetNext(pos); if (::IsWindowVisible(layout.hWnd)) - ClipChildWindow(layout.hWnd, pRegion); + ClipChildWindow(layout, pRegion); } pos = m_listLayoutCB.GetHeadPosition(); while (pos != NULL) @@ -241,7 +233,7 @@ void CResizableLayout::GetClippingRegion(CRgn* pRegion) continue; if (::IsWindowVisible(layout.hWnd)) - ClipChildWindow(layout.hWnd, pRegion); + ClipChildWindow(layout, pRegion); } // fix for RTL layouts (1 pixel too-much) @@ -290,78 +282,129 @@ void CResizableLayout::GetTotalClientRect(LPRECT lpRect) GetResizableWnd()->GetClientRect(lpRect); } -BOOL CResizableLayout::NeedsRefresh(HWND hWnd) +BOOL CResizableLayout::NeedsRefresh(const CResizableLayout::LayoutInfo& layout, + const CRect& rectOld, const CRect& rectNew) { - // get window class - CString str; - ::GetClassName(hWnd, str.GetBufferSetLength(MAX_PATH), MAX_PATH); - str.ReleaseBuffer(); + BOOL bSameWidth = (rectNew.Width() == rectOld.Width()); + BOOL bSameHeight = (rectNew.Height() == rectOld.Height()); + + if (bSameWidth && bSameHeight) + return FALSE; // optimistic, no need to refresh BOOL bRefresh = FALSE; // window classes that need refresh when resized - if (str == "Static") + if (layout.sWndClass == "Static") { - DWORD style = ::GetWindowLong(hWnd, GWL_STYLE); + DWORD style = ::GetWindowLong(layout.hWnd, GWL_STYLE); switch (style & SS_TYPEMASK) { case SS_LEFT: case SS_CENTER: case SS_RIGHT: + // word-wrapped text + bRefresh = bRefresh || !bSameWidth; + // vertically centered text + if (style & SS_CENTERIMAGE) + bRefresh = bRefresh || !bSameHeight; + break; + + case SS_LEFTNOWORDWRAP: + // text with ellipsis + if (style & SS_ELLIPSISMASK) + bRefresh = bRefresh || !bSameWidth; + // vertically centered text + if (style & SS_CENTERIMAGE) + bRefresh = bRefresh || !bSameHeight; + break; + case SS_ENHMETAFILE: case SS_BITMAP: - // word-wrapped text needs bRefresh + case SS_ICON: + // images + case SS_BLACKFRAME: + case SS_GRAYFRAME: + case SS_WHITEFRAME: + case SS_ETCHEDFRAME: + // and frames bRefresh = TRUE; break; + } + } - case SS_ICON: - case SS_SIMPLE: - case SS_ETCHEDHORZ: - case SS_ETCHEDVERT: - break; + // window classes that don't redraw client area correctly + // when the hor scroll pos changes due to a resizing + BOOL bHScroll = FALSE; + if (layout.sWndClass == "ListBox") + bHScroll = TRUE; - default: - // centered images or text need bRefresh - if (style & SS_CENTERIMAGE) - bRefresh = TRUE; + // fix for horizontally scrollable windows + if (!bSameWidth && bHScroll) + { + // get max scroll position + SCROLLINFO info; + info.cbSize = sizeof(SCROLLINFO); + info.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; + if (::GetScrollInfo(layout.hWnd, SB_HORZ, &info)) + { + // subtract the page size + info.nMax -= __max(info.nPage-1,0); + } + // increment in width + int nDiff = rectNew.Width() - rectOld.Width(); + + // resizing will cause the text to scroll on the right + // because the scrollbar is going beyond the right limit + if ((info.nMax > 0) && (nDiff > 0) && (info.nPos + nDiff > info.nMax)) + { + // needs repainting, due to horiz scrolling + bRefresh = TRUE; } } return bRefresh; } -BOOL CResizableLayout::LikesClipping(HWND hWnd) +BOOL CResizableLayout::LikesClipping(const CResizableLayout::LayoutInfo& layout) { - // check child type - CString str; - ::GetClassName(hWnd, str.GetBufferSetLength(MAX_PATH), MAX_PATH); - str.ReleaseBuffer(); - - DWORD style = ::GetWindowLong(hWnd, GWL_STYLE); + DWORD style = ::GetWindowLong(layout.hWnd, GWL_STYLE); // skip windows that wants background repainted - if (str == TOOLBARCLASSNAME && (style & TBSTYLE_TRANSPARENT)) + if (layout.sWndClass == TOOLBARCLASSNAME && (style & TBSTYLE_TRANSPARENT)) return FALSE; - if (str == "Button" && (style & BS_TYPEMASK) == BS_GROUPBOX) + if (layout.sWndClass == "Button" && (style & BS_TYPEMASK) == BS_GROUPBOX) return FALSE; - if (str == "Static") + if (layout.sWndClass == "Static") { switch (style & SS_TYPEMASK) { + case SS_LEFT: + case SS_CENTER: + case SS_RIGHT: + case SS_SIMPLE: + case SS_LEFTNOWORDWRAP: + // text case SS_BLACKRECT: case SS_GRAYRECT: case SS_WHITERECT: + // filled rects case SS_ETCHEDHORZ: case SS_ETCHEDVERT: + // etched lines case SS_BITMAP: + // bitmaps + return TRUE; break; + case SS_ICON: case SS_ENHMETAFILE: if (style & SS_CENTERIMAGE) return FALSE; + return TRUE; break; + default: return FALSE; } @@ -371,16 +414,16 @@ BOOL CResizableLayout::LikesClipping(HWND hWnd) return TRUE; } -void CResizableLayout::CalcNewChildPosition(const CRect &rectParent, CResizableLayout::LayoutInfo &layout, CRect &rectChild, UINT& uFlags) +void CResizableLayout::CalcNewChildPosition(const CResizableLayout::LayoutInfo& layout, + const CRect &rectParent, CRect &rectChild, UINT& uFlags) { CWnd* pParent = GetResizableWnd(); - CRect rectNew; - CWnd* pWnd = CWnd::FromHandle(layout.hWnd); // temporary solution - - pWnd->GetWindowRect(&rectChild); + ::GetWindowRect(layout.hWnd, &rectChild); ::MapWindowPoints(NULL, pParent->m_hWnd, (LPPOINT)&rectChild, 2); + CRect rectNew; + // calculate new top-left corner rectNew.left = layout.sizeMarginTL.cx + rectParent.Width() * layout.sizeTypeTL.cx / 100; rectNew.top = layout.sizeMarginTL.cy + rectParent.Height() * layout.sizeTypeTL.cy / 100; @@ -389,23 +432,12 @@ void CResizableLayout::CalcNewChildPosition(const CRect &rectParent, CResizableL rectNew.right = layout.sizeMarginBR.cx + rectParent.Width() * layout.sizeTypeBR.cx / 100; rectNew.bottom = layout.sizeMarginBR.cy + rectParent.Height() * layout.sizeTypeBR.cy / 100; - // fix for horizontally scrollable windows - if (layout.bAdjHScroll && !rectNew.EqualRect(&rectChild)) - { - int diff = rectNew.Width() - rectChild.Width(); - int max = pWnd->GetScrollLimit(SB_HORZ); - - layout.bNeedRefresh = FALSE; - if (max > 0 && pWnd->GetScrollPos(SB_HORZ) > max - diff) - { - // needs repainting, due to horiz scrolling - layout.bNeedRefresh = TRUE; - } - } + // window classes that need refresh when resized + BOOL bRefresh = NeedsRefresh(layout, rectChild, rectNew); // set flags uFlags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION; - if (layout.bNeedRefresh) + if (bRefresh) uFlags |= SWP_NOCOPYBITS; if (rectNew.TopLeft() == rectChild.TopLeft()) uFlags |= SWP_NOMOVE; diff --git a/ResizableLib/ResizableLayout.h b/ResizableLib/ResizableLayout.h index e46501c..e37fd9c 100644 --- a/ResizableLib/ResizableLayout.h +++ b/ResizableLib/ResizableLayout.h @@ -40,8 +40,7 @@ class CResizableLayout HWND hWnd; UINT nCallbackID; - BOOL bAdjHScroll; - BOOL bNeedRefresh; + CString sWndClass; // upper-left corner SIZE sizeTypeTL; @@ -52,42 +51,35 @@ class CResizableLayout SIZE sizeMarginBR; public: - LayoutInfo() - { ZeroMemory(this, sizeof(LayoutInfo)); } + LayoutInfo() : hWnd(NULL), nCallbackID(0) + { } LayoutInfo(HWND hwnd, SIZE tl_t, SIZE tl_m, - SIZE br_t, SIZE br_m, BOOL hscroll, BOOL refresh) - { - hWnd = hwnd; - nCallbackID = 0; - - bAdjHScroll = hscroll; - bNeedRefresh = refresh; - - sizeTypeTL = tl_t; - sizeMarginTL = tl_m; - - sizeTypeBR = br_t; - sizeMarginBR = br_m; - } + SIZE br_t, SIZE br_m, CString classname) + : hWnd(hwnd), nCallbackID(0), sWndClass(classname), + sizeTypeTL(tl_t), sizeMarginTL(tl_m), + sizeTypeBR(br_t), sizeMarginBR(br_m) + { } }; private: // list of repositionable controls - CMap m_mapLayout; + CMap m_mapLayout; + CList m_listLayout; CList m_listLayoutCB; - void ClipChildWindow(HWND hWnd, CRgn* pRegion); + void ClipChildWindow(const CResizableLayout::LayoutInfo &layout, CRgn* pRegion); - void CalcNewChildPosition(const CRect &rectParent, - LayoutInfo &layout, CRect &rectChild, UINT& uFlags); + void CalcNewChildPosition(const CResizableLayout::LayoutInfo &layout, + const CRect &rectParent, CRect &rectChild, UINT& uFlags); protected: // override to specify clipping for custom or unsupported windows - virtual BOOL LikesClipping(HWND hWnd); + virtual BOOL LikesClipping(const CResizableLayout::LayoutInfo &layout); // override to specify refresh for custom or unsupported windows - virtual BOOL NeedsRefresh(HWND hWnd); + virtual BOOL NeedsRefresh(const CResizableLayout::LayoutInfo &layout, + const CRect &rectOld, const CRect &rectNew); // paint the background on the given DC (for XP theme's compatibility) void EraseBackground(CDC* pDC); @@ -118,11 +110,11 @@ class CResizableLayout BOOL GetAnchorPosition(HWND hWnd, const CRect &rectParent, CRect &rectChild, UINT& uFlags) { - LayoutInfo layout; - if (!m_mapLayout.Lookup(hWnd, layout)) + POSITION pos; + if (!m_mapLayout.Lookup(hWnd, pos)) return FALSE; - CalcNewChildPosition(rectParent, layout, rectChild, uFlags); + CalcNewChildPosition(m_listLayout.GetAt(pos), rectParent, rectChild, uFlags); return TRUE; } @@ -135,21 +127,27 @@ class CResizableLayout } // remove an anchored control from the layout, given its HWND - void RemoveAnchor(HWND hWnd) + BOOL RemoveAnchor(HWND hWnd) { - m_mapLayout.RemoveKey(hWnd); + POSITION pos; + if (!m_mapLayout.Lookup(hWnd, pos)) + return FALSE; + + m_listLayout.RemoveAt(pos); + return m_mapLayout.RemoveKey(hWnd); } // remove an anchored control from the layout, given its HWND - void RemoveAnchor(UINT nID) + BOOL RemoveAnchor(UINT nID) { - RemoveAnchor(::GetDlgItem(GetResizableWnd()->GetSafeHwnd(), nID)); + return RemoveAnchor(::GetDlgItem(GetResizableWnd()->GetSafeHwnd(), nID)); } // reset layout content void RemoveAllAnchors() { m_mapLayout.RemoveAll(); + m_listLayout.RemoveAll(); m_listLayoutCB.RemoveAll(); } @@ -157,7 +155,7 @@ class CResizableLayout void ArrangeLayout(); // override to provide dynamic control's layout info - virtual BOOL ArrangeLayoutCallback(LayoutInfo& layout); + virtual BOOL ArrangeLayoutCallback(CResizableLayout::LayoutInfo& layout); // override to provide the parent window virtual CWnd* GetResizableWnd() = 0;