Author Topic: ANCHOR CONSTANTS  (Read 5087 times)

0 Members and 1 Guest are viewing this topic.

Offline Patrice Terrier

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2391
  • Gender: Male
    • www.zapsolution.com
ANCHOR CONSTANTS
« on: March 27, 2010, 11:16:23 AM »
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:

Code: [Select]
%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:



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.

...
« Last Edit: July 20, 2014, 10:57:39 AM by Patrice Terrier »
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Offline Patrice Terrier

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2391
  • Gender: Male
    • www.zapsolution.com
ANCHOR CONSTANTS (how it works)
« Reply #1 on: July 20, 2014, 11:41:42 AM »
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:
Code: [Select]
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:
Code: [Select]
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.

...
« Last Edit: July 20, 2014, 11:59:46 AM by Patrice Terrier »
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Offline James C. Fuller

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 672
Re: ANCHOR CONSTANTS
« Reply #2 on: July 20, 2014, 03:36:21 PM »
Patrice,
  Thank you for the code.
  Is this code supposed to be stand-alone?  Can't locate CheckWindowStyle !

James

Offline Patrice Terrier

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2391
  • Gender: Male
    • www.zapsolution.com
Re: ANCHOR CONSTANTS
« Reply #3 on: July 20, 2014, 03:42:51 PM »
Quote
Can't locate CheckWindowStyle !

Here it is:

C++
Code: [Select]
long CheckWindowStyle(IN HWND hWnd, IN LONG_PTR nStyle) {
    long nRet = 0;
    if ((GetWindowLongPtr(hWnd, GWL_STYLE) & nStyle) == nStyle) { nRet = -1; }
    return nRet;
}


PowerBASIC
Code: [Select]
FUNCTION CheckWindowStyle(BYVAL hWnd AS LONG, BYVAL nStyle AS LONG) AS LONG
    IF (GetWindowLong(hWnd, %GWL_STYLE) AND nStyle) = nStyle THEN FUNCTION = -1
END FUNCTION
« Last Edit: July 21, 2014, 08:12:03 AM by Patrice Terrier »
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Offline James C. Fuller

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 672
Re: ANCHOR CONSTANTS
« Reply #4 on: July 20, 2014, 04:38:56 PM »
Thank you kind sir.
Preliminary PB test works as expected.

James

Offline Patrice Terrier

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2391
  • Gender: Male
    • www.zapsolution.com
Re: ANCHOR CONSTANTS
« Reply #5 on: July 21, 2014, 09:29:05 AM »
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.

...
« Last Edit: July 21, 2014, 11:44:22 AM by Patrice Terrier »
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Offline Patrice Terrier

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2391
  • Gender: Male
    • www.zapsolution.com
Re: ANCHOR CONSTANTS
« Reply #6 on: July 21, 2014, 12:09:59 PM »
Here is a link to a demo showing the importance to preserve the z-order when rendering mutiple transparent layers, altogether with anchor properties.

...
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Offline Pierre Bellisle

  • Jr. Member
  • **
  • Posts: 72
Re: ANCHOR CONSTANTS
« Reply #7 on: July 21, 2014, 01:39:27 PM »
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
« Last Edit: July 22, 2014, 04:39:26 PM by Pierre Bellisle »