The WinLIFT "skSetAnchorControl" API has been written specifically to anchor/resize child controls within their parent container, it has been checked thoroughly with FF3, DDT, SDK, C#, C++, WinDev, and GDImage sprite.
Using skSetAnchorControl is very easy, you just have to provide the handle of the specific child control and use one of the CONSTANT below:
%ANCHOR_NONE = 0
%ANCHOR_WIDTH = 1
%ANCHOR_RIGHT = 2
%ANCHOR_CENTER_HORZ = 3
%ANCHOR_HEIGHT = 4
%ANCHOR_HEIGHT_WIDTH = 5
%ANCHOR_HEIGHT_RIGHT = 6
%ANCHOR_BOTTOM = 7
%ANCHOR_BOTTOM_WIDTH = 8
%ANCHOR_BOTTOM_RIGHT = 9
%ANCHOR_CENTER_HORZ_BOTTOM = 10
%ANCHOR_CENTER_VERT = 11
%ANCHOR_CENTER_VERT_RIGHT = 12
%ANCHOR_CENTER = 13
To help you using the good one, print this:
(http://www.zapsolution.com/preview/anchor.gif)
Note: Some third party addon provide a similar API.
However, because the SkinEngine works in composited mode, it is very important to use skSetAnchorControl to preserve the transparency effect and smooth resize.
...
For those wanting to learn the insight, here is a {simplified} version of the WinLIFT anchor subroutines.
skSetAnchorMode must be used to setup the anchor property of a child control.
AnchorEnum is the central callback function that must be used for each of the messages that could affect the size of the parent window.
PowerBASIC code:TYPE ANCHORPROPERTY
hWnd AS DWORD
anchor AS LONG
rc AS RECT
centerx AS LONG
centery AS LONG
END TYPE
GLOBAl g_Prop() AS ANCHORPROPERTY
FUNCTION skPopupOwner (BYVAL hWnd AS DWORD) AS DWORD
LOCAL hParent AS LONG
IF (IsWindow(hWnd)) THEN
DO
hParent = GetParent(hWnd)
IF hParent = 0 THEN hParent = hWnd: EXIT DO
IF CheckWindowStyle(hParent, %WS_POPUP) THEN EXIT DO
hWnd = hParent
LOOP
FUNCTION = hParent
END IF
END FUNCTION
'// Anchor item detection.
FUNCTION AnchorItem (BYVAL hWnd AS DWORD) AS LONG
LOCAL nItem AS LONG
IF UBOUND(g_Prop) > 0 THEN
ARRAY SCAN g_Prop(), FROM 1 TO 4, = MKL$(hWnd), TO nItem
END IF
FUNCTION = nItem
END FUNCTION
'// Anchor properties setup.
FUNCTION skSetAnchorMode (BYVAL hWnd AS DWORD, BYVAL AnchorMode AS LONG) AS LONG
LOCAL pZP AS LONG
IF hWnd THEN
LOCAL rc AS RECT, pr AS RECT, p AS POINTAPI
pZP = AnchorItem(hWnd)
IF pZP = 0 THEN ' If the object already exist then we ReUse it
pZP = MAX&(UBOUND(g_Prop) + 1, 1)
REDIM PRESERVE g_Prop(1 TO pZP) AS ANCHORPROPERTY
END IF
g_Prop(pZP).hWnd = hWnd
GetWindowRect(hWnd, rc)
p.X = rc.nLeft: p.Y = rc.nTop
ScreenToClient(Getparent(hWnd), p)
GetClientRect(Getparent(hWnd), pr)
g_Prop(pZP).anchor = MIN&(MAX&(AnchorMode, %ANCHOR_NONE), %ANCHOR_CENTER)
g_Prop(pZP).rc.nLeft = p.X
g_Prop(pZP).rc.nTop = p.Y
g_Prop(pZP).rc.nRight = pr.nRight - (rc.nRight - rc.nLeft + p.X)
g_Prop(pZP).rc.nBottom = pr.nBottom - (rc.nBottom - rc.nTop + p.Y)
g_Prop(pZP).centerx = p.X - (pr.nRight \ 2)
g_Prop(pZP).centery = p.Y - (pr.nBottom \ 2)
FUNCTION = -1
END IF
END FUNCTION
'// Anchor enum callback function.
FUNCTION AnchorEnum (BYVAL hWnd AS LONG, BYVAL lParam AS LONG) AS LONG
IF IsIconic(skPopupOwner(hWnd)) THEN FUNCTION = %FALSE: EXIT FUNCTION '// Stop enumeration
LOCAL pr AS RECT, rw AS RECT, nW, nH, pZP AS LONG
LOCAL x, y, xW, yH AS LONG
pZP = AnchorItem(hWnd)
IF pZP THEN
IF g_Prop(pZP).anchor > %ANCHOR_NONE THEN
GetWindowRect(hWnd, rw)
nW = rw.nRight - rw.nLeft
nH = rw.nBottom - rw.nTop
GetClientRect(GetParent(hWnd), pr)
x = 0: y = 0: xW = 0: yH = 0
SELECT CASE LONG g_Prop(pZP).anchor
'CASE %ANCHOR_NONE '= 0
CASE %ANCHOR_WIDTH '= 1
x = g_Prop(pZP).rc.nLeft
y = g_Prop(pZP).rc.nTop
xW = MAX&(pr.nRight - g_Prop(pZP).rc.nLeft - g_Prop(pZP).rc.nRight, 0)
yH = nH
CASE %ANCHOR_RIGHT '= 2
x = pr.nRight - nW - g_Prop(pZP).rc.nRight
y = g_Prop(pZP).rc.nTop
xW = nW
yH = nH
CASE %ANCHOR_CENTER_HORZ '= 3
x = (pr.nRight \ 2) + g_Prop(pZP).centerx
y = g_Prop(pZP).rc.nTop
xW = nW
yH = nH
CASE %ANCHOR_HEIGHT '= 4
x = g_Prop(pZP).rc.nLeft
y = g_Prop(pZP).rc.nTop
xW = nW
yH = MAX&(pr.nBottom - g_Prop(pZP).rc.nTop - g_Prop(pZP).rc.nBottom, 0)
CASE %ANCHOR_HEIGHT_WIDTH '= 5
x = g_Prop(pZP).rc.nLeft
y = g_Prop(pZP).rc.nTop
xW = MAX&(pr.nRight - g_Prop(pZP).rc.nLeft - g_Prop(pZP).rc.nRight, 0)
yH = MAX&(pr.nBottom - g_Prop(pZP).rc.nTop - g_Prop(pZP).rc.nBottom, 0)
CASE %ANCHOR_HEIGHT_RIGHT '= 6
x = pr.nRight - nW - g_Prop(pZP).rc.nRight
y = g_Prop(pZP).rc.nTop
xW = nW
yH = MAX&(pr.nBottom - g_Prop(pZP).rc.nTop - g_Prop(pZP).rc.nBottom, 0)
CASE %ANCHOR_BOTTOM '= 7
x = g_Prop(pZP).rc.nLeft
y = pr.nBottom - g_Prop(pZP).rc.nBottom - nH
xW = nW
yH = nH
CASE %ANCHOR_BOTTOM_WIDTH '= 8
x = g_Prop(pZP).rc.nLeft
y = pr.nBottom - g_Prop(pZP).rc.nBottom - nH
xW = MAX&(pr.nRight - g_Prop(pZP).rc.nLeft - g_Prop(pZP).rc.nRight, 0)
yH = nH
CASE %ANCHOR_BOTTOM_RIGHT '= 9
x = pr.nRight - nW - g_Prop(pZP).rc.nRight
y = pr.nBottom - g_Prop(pZP).rc.nBottom - nH
xW = nW
yH = nH
CASE %ANCHOR_CENTER_HORZ_BOTTOM '= 10
x = (pr.nRight \ 2) + g_Prop(pZP).centerx
y = pr.nBottom - g_Prop(pZP).rc.nBottom - nH
xW = nW
yH = nH
CASE %ANCHOR_CENTER_VERT '= 11
x = g_Prop(pZP).rc.nLeft
y = (pr.nBottom - nH) \ 2
xW = nW
yH = nH
CASE %ANCHOR_CENTER_VERT_RIGHT '= 12
x = pr.nRight - nW - g_Prop(pZP).rc.nRight
y = (pr.nBottom - nH) \ 2
xW = nW
yH = nH
CASE %ANCHOR_CENTER '= 13
x = (pr.nRight \ 2) + g_Prop(pZP).centerx
y = (pr.nBottom \ 2) + g_Prop(pZP).centery
xW = nW
yH = nH
END SELECT
MoveWindow(hWnd, x, y, xW, yH, 0)
END IF
END IF
FUNCTION = %TRUE '// Continue enumeration of children
END FUNCTION
C++ code:struct ANCHORPROPERTY {
HWND hWnd;
long anchor;
RECT rc;
long centerx;
long centery;
};
#define UBOUND(T) (T.size())
vector<ANCHORPROPERTY> g_Prop;
HWND skPopupOwner (IN HWND hWnd) { // dllexport
HWND nRet = 0;
if (IsWindow(hWnd)) {
HWND hParent = 0;
do {
hParent = GetParent(hWnd);
if (hParent == 0) { hParent = hWnd; break; }
if (CheckWindowStyle(hParent, WS_POPUP)) { break; }
hWnd = hParent; }
while (-1);
nRet = hParent;
}
return nRet;
}
long AnchorItem (IN HWND hWnd) {
long nItem = -1;
long UB = (long) UBOUND(g_Prop);
if (UB > 0) {
long K = 0;
for (K = 0; K < UB; ++K) {
if (g_Prop[K].hWnd == hWnd) { nItem = K; break; }
}
}
return nItem;
}
long skSetAnchorMode (IN HWND hWnd, IN long AnchorMode) {
long nRet = 0;
if (hWnd) {
RECT rc = {0}, pr = {0}; POINT p = {0};
long pZP = AnchorItem(hWnd);
if (pZP < 0) { // if (the object already exist) then we ReUse it
pZP = (long) (UBOUND(g_Prop));
g_Prop.resize(pZP + 1);
}
g_Prop[pZP].hWnd = hWnd;
GetWindowRect(hWnd, &rc);
p.x = rc.left; p.y = rc.top;
ScreenToClient(GetParent(hWnd), &p);
GetClientRect(GetParent(hWnd), &pr);
g_Prop[pZP].anchor = min(max(AnchorMode, ANCHOR_NONE), ANCHOR_CENTER);
g_Prop[pZP].rc.left = p.x;
g_Prop[pZP].rc.top = p.y;
g_Prop[pZP].rc.right = pr.right - (rc.right - rc.left + p.x);
g_Prop[pZP].rc.bottom = pr.bottom - (rc.bottom - rc.top + p.y);
g_Prop[pZP].centerx = p.x - (pr.right / 2);
g_Prop[pZP].centery = p.y - (pr.bottom / 2);
nRet = -1;
}
return nRet;
}
BOOL CALLBACK AnchorEnum (IN HWND hWnd, IN LPARAM lParam) {
if (IsIconic(skPopupOwner(hWnd))) { return FALSE; } // Stop enumeration
long pZP = AnchorItem(hWnd);
if (pZP > -1) {
if (g_Prop[pZP].anchor > ANCHOR_NONE) {
RECT pr, rw;
GetWindowRect(hWnd, &rw);
long nW = rw.right - rw.left;
long nH = rw.bottom - rw.top;
GetClientRect(GetParent(hWnd), &pr);
long x = 0, y = 0, xW = 0, yH = 0;
switch (g_Prop[pZP].anchor) {
case ANCHOR_WIDTH: //= 1
x = g_Prop[pZP].rc.left;
y = g_Prop[pZP].rc.top;
xW = max(pr.right - g_Prop[pZP].rc.left - g_Prop[pZP].rc.right, 0);
yH = nH;
break;
case ANCHOR_RIGHT: //= 2
x = pr.right - nW - g_Prop[pZP].rc.right;
y = g_Prop[pZP].rc.top;
xW = nW;
yH = nH;
break;
case ANCHOR_CENTER_HORZ: //= 3
x = (pr.right / 2) + g_Prop[pZP].centerx;
y = g_Prop[pZP].rc.top;
xW = nW;
yH = nH;
break;
case ANCHOR_HEIGHT: //= 4
x = g_Prop[pZP].rc.left;
y = g_Prop[pZP].rc.top;
xW = nW;
yH = max(pr.bottom - g_Prop[pZP].rc.top - g_Prop[pZP].rc.bottom, 0);
break;
case ANCHOR_HEIGHT_WIDTH: //= 5
x = g_Prop[pZP].rc.left;
y = g_Prop[pZP].rc.top;
xW = max(pr.right - g_Prop[pZP].rc.left - g_Prop[pZP].rc.right, 0);
yH = max(pr.bottom - g_Prop[pZP].rc.top - g_Prop[pZP].rc.bottom, 0);
break;
case ANCHOR_HEIGHT_RIGHT: //= 6
x = pr.right - nW - g_Prop[pZP].rc.right;
y = g_Prop[pZP].rc.top;
xW = nW;
yH = max(pr.bottom - g_Prop[pZP].rc.top - g_Prop[pZP].rc.bottom, 0);
break;
case ANCHOR_BOTTOM: //= 7
x = g_Prop[pZP].rc.left;
y = pr.bottom - g_Prop[pZP].rc.bottom - nH;
xW = nW;
yH = nH;
break;
case ANCHOR_BOTTOM_WIDTH: //= 8
x = g_Prop[pZP].rc.left;
y = pr.bottom - g_Prop[pZP].rc.bottom - nH;
xW = max(pr.right - g_Prop[pZP].rc.left - g_Prop[pZP].rc.right, 0);
yH = nH;
break;
case ANCHOR_BOTTOM_RIGHT: //= 9
x = pr.right - nW - g_Prop[pZP].rc.right;
y = pr.bottom - g_Prop[pZP].rc.bottom - nH;
xW = nW;
yH = nH;
break;
case ANCHOR_CENTER_HORZ_BOTTOM: //= 10
x = (pr.right / 2) + g_Prop[pZP].centerx;
y = pr.bottom - g_Prop[pZP].rc.bottom - nH;
xW = nW;
yH = nH;
break;
case ANCHOR_CENTER_VERT: //= 11
x = g_Prop[pZP].rc.left;
y = (pr.bottom - nH) / 2;
xW = nW;
yH = nH;
break;
case ANCHOR_CENTER_VERT_RIGHT: //= 12
x = pr.right - nW - g_Prop[pZP].rc.right;
y = (pr.bottom - nH) / 2;
xW = nW;
yH = nH;
break;
case ANCHOR_CENTER: //= 13
x = (pr.right / 2) + g_Prop[pZP].centerx;
y = (pr.bottom / 2) + g_Prop[pZP].centery;
xW = nW;
yH = nH;
break;
}
MoveWindow(hWnd, x, y, xW, yH, 0);
}
}
return TRUE; // Continue enumeration of children
}
NOTE: in skPopupOwner you can boost the detection of the parent, if you know it already.
...
Patrice,
Thank you for the code.
Is this code supposed to be stand-alone? Can't locate CheckWindowStyle !
James
QuoteCan't locate CheckWindowStyle !
Here it is:
C++long CheckWindowStyle(IN HWND hWnd, IN LONG_PTR nStyle) {
long nRet = 0;
if ((GetWindowLongPtr(hWnd, GWL_STYLE) & nStyle) == nStyle) { nRet = -1; }
return nRet;
}
PowerBASICFUNCTION CheckWindowStyle(BYVAL hWnd AS LONG, BYVAL nStyle AS LONG) AS LONG
IF (GetWindowLong(hWnd, %GWL_STYLE) AND nStyle) = nStyle THEN FUNCTION = -1
END FUNCTION
Thank you kind sir.
Preliminary PB test works as expected.
James
Pierre--
1 - You should have asked me first, before posting my code on the PowerBASIC forum.
It should be posted here in this thread to let me explain how to use it.
2 - With WinLIFT and GDImage, MoveWindow, should not be set to %TRUE, and it should not be followed by InvalidateRect, because this would send a WM_PAINT message, and while this would work for DDT code, it won't work well with SDK code working in composited mode. This is the consequence of the low priority of the WM_PAINT message with the side effect that some of the child controls are not always redrawn with full respect of their z-order.
3 - James is correct, EnumChildWindows(CBHNDL,CODEPTR(AnchorEnum),1), must be used only once during the WM_SIZE message.
Despiste a recent discussion about the use of GLSL and the GPU on the PowerBASIC forum, i keep saying that in the case of WinLIFT and GDImage that are using the DWM, ALL the drawings are performed by the GPU, this is called the AERO mode (composited mode).
It means that the whole desktop, with all the activ popup windows (and their child controls) are rendered onto a hidden DirectDraw surface (including OpenGL) and refreshed at a fixed rate of 60 FPS.
Starting with Windows 8, the composited mode is now always turned On, and it could not be turned Off anymore. And the transparent layered mode can now also be used with child controls (before only with popup window).
In order to render correctly the transparent layers, on the DirectDraw surface, each popup window, and each child controls inside of them must be redrawn with strict respect of their z-order.
Both WinLIFT and GDImage are using a special function to ensure that everything is redrawn correctly, including the transparent sprite objects inside of a GDImage graphic container. This is the reason why inside of the AnchorEnum callback, the last parameter of MoveWindow should be set to FALSE and not be followed by InvalidateRect. Of course all of this could be ignored in the case of DDT, but we are speaking of SDK code here, aren't we? :)
Note: When using WinLIFT (and DDT together) it is very important to use the default skSetAnchorControl API, and never those of third party addon providers.
...
Here is a link to a demo (http://www.jose.it-berater.org/smfforum/index.php?topic=4896.0) showing the importance to preserve the z-order when rendering mutiple transparent layers, altogether with anchor properties.
...
A link for those who might find that a part of this discussion is hard to follow...
http://www.powerbasic.com/support/pbforums/showpost.php?p=458838&postcount=27
Pierre