Hi James,
I worked on it several hours today and it appears the VC19 built window is getting truncated to the tune of several pixels. If you compare two windows side by side from VC19 and anything else the VC19 window is noticeably less in height. Also, I realized MS keeps adding metrics to GetSystemMetrics. I'm attaching my work from today. It is the same app as your code. I put piles of output log statements in a version to output the system metrics of piles of stuff. Compare "Output_VC9.txt" to "Output_VC15.txt". The only difference you'll see is ...
iCxPaddedBorder
...which is gotten through SM_CXPADDEDBORDER. It comes out as 0 for VC9 and 4 for VC19. Also, the caption/title bar is larger on VC19 even though GetSystemMetrics is showing no change between compilers. Its time for me to go to bed but it would be interesting to output GetWindowRect() values between a VC19 window and anything else. Attached is my app that generated those output files. I tested with #define TCLib and #define Debug. You'll be able to figure out what to do. 'till tomorrow.
Fred
PS - gmail won't let me send zip. I'll post in Jose's Forum
See my answer there
http://www.objreader.com/index.php?topic=73.msg313#msg313
Fred,
VC9??
I tried with c++ cl 18. and it was the same as cl 19.
I do not have any older version to try.
James
Quote
VC9??
I know the version numbers MS uses are confusing. My Visual Studio 2008 actually installs its C/C++ files in a directory VC9. However, if you look at the compiler version output during a typical command line compile, it states VC15. So there are three somewhat unrelated numbers there.
With Visual Studio Community 2015 the the compiler version number output during a compile is 19!
I just saw your reply Patrice. Thanks for that info. Like James I wasn't familiar with that function. What I was thinking one must do would be to use all the different values returned by GetSystemMetrics() to kind of 'back calculate' what the parameters of the CreateWindowEx() call should be for a specific Client Rectangle. But that 'Adjust' function does it for you.
For me, I'm not very enamored of Visual Studio Community 2015. I sweat blood for a week last winter due to a documented compiler bug when I was doing my TCLib work. The documented bug had to do with Microsoft's attempt to create something like PowerBASIC's Dim At for Classes. The C++ standard keeps changing, as its user base seems to want to morph it into C#, seems to me.
I'm going to do this (thanks to José Roca's AfxWin). Use zeros for width and height and then call this.
Can also be used for dpi aware app's.
I'd still would like to know what is going on!!
James
void SetClientSize (HWND, long, int, float = 1, float = 1);
void SetClientSize (HWND hwnd, long nWidth, int nHeight, float rxRatio, float ryRatio)
{
RECT rc = {0};
RECT rcTemp = {0};
// Convert the client rectangle to a window rectangle.
// The AdjustWindowRectEx function cannot take menu wrapping into account
// because it doesn't know which menu we are using.
SetRect( &rc, 0, 0, nWidth * rxRatio, nHeight * ryRatio);
HANDLE hMenu = GetMenu(hwnd);
DWORD dwStyle = GetWindowLongPtr(hwnd, GWL_STYLE);
AdjustWindowRectEx( &rc, dwStyle, (hMenu != NULL), GetWindowLongPtr(hwnd, GWL_EXSTYLE));
// If there is a menu, we need to check how much wrapping occurs when we set
// the window to the width specified by AdjustWindowRectEX and an infinite
// amount of height. An infinite height allows us to see every single menu wrap.
if(hMenu != NULL )
{
rcTemp = rc;
rcTemp.bottom = 0x7FFF; // "Infinite" height
SendMessage(hwnd, (UINT)WM_NCCALCSIZE, (WPARAM)0, (LPARAM)&rcTemp);
// Adjust our previous calculation to compensate for menu wrapping.
rc.bottom += rcTemp.top;
}
// The AdjustWindowRectEx function does not take the WS_VSCROLL or WS_HSCROLL
// styles into account. To account for the scroll bars, we need to call the
// GetSystemMetrics function with SM_CXVSCROLL or SM_CYHSCROLL.
if((dwStyle & WS_HSCROLL) == WS_HSCROLL )
{
rc.bottom = rc.bottom + GetSystemMetrics( SM_CYHSCROLL);
}
if((dwStyle & WS_VSCROLL) == WS_VSCROLL )
{
rc.right = rc.right + GetSystemMetrics( SM_CXVSCROLL);
}
long cx = rc.right - rc.left;
long cy = rc.bottom - rc.top;
SetWindowPos(hwnd, NULL, 0, 0, cx, cy, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
}
I worked on it some more Jim, as I found the whole thing curious, but I had to get back to my work. There are several things I'm not sure about.
For one, I find it curious that two different versions of Visual Studio (my VStudio 2008 and VStudio 2015) create graphical components with different metrics on the same operating system and same computer. I was using a Win 10 Home HP Envy laptop. And of course I have both versions of Visual Studio installed on that box. I guess both have the SDK installed/available that came with each respective version, and with each version would also have the necessary *.lib files. I can see how those might be different, but they are each linking with the same basic dlls not? That is, gdi32.dll??? As one of the respondents at CodeGuru mentioned, there must be something going on with manifests or calls to the dlls to tell them to react different for one call from another.
Another thing I find strange is that when you compare the windows side by side the title bars are obviously of a different height. I mean, its visually not even a close call. On the VS2015 the title bar is unmistakingly deeper. Yet the calls to GetSystemMetrics to retrieve that height reveals them the same for both windows. I was able to piece everything together in terms of sizes with GetSystemMetrics calls for my VS 2008 windows, but not for the VS2015 windows.
In any case, the code Patrice and Jose provided seems to resolve the issue, so I don't know if there is any more point in wondering about it.
The answer is quite obvious, Visual Studio 2015 uses the latest SDK (the one from Windows 8+ 10)
while 2008 version uses the older SDK.
There are many new API starting with the SDK for Windows 8+, and of course they are not available with previous SDK build.
For example the touchscreen API is much simpler with the latest SDK.
Patrice,
Yes different SDK's but they are all accessing the same User32.dll ?
I did a loadlibray and still got the same results???
James
typedef HWND (__stdcall *BCXFPROT1)(DWORD, LPCSTR, LPCSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID);
// **********[ DLL Declarations ]**********
HMODULE H_USER32 = LoadLibrary("User32.dll");
if(H_USER32 == NULL) {
MessageBox (GetActiveWindow(), "Failed to load library \nUser32.dll", "Load Library Error", 0 );
return 1;
}
bc9CreateWindow = (BCXFPROT1)GetProcAddress(H_USER32, "CreateWindowExA");
if(bc9CreateWindow == NULL) {
MessageBox (GetActiveWindow(), "Failed to find process \nbc9CreateWindow", "Find Process Error", 0 );
return 1;
}
// ****************************************
hWin = bc9CreateWindow( dwStyleEx, szClassName, "What is your name? ", dwStyle, ( GetSystemMetrics( SM_CXSCREEN) - 246) / 2, ( GetSystemMetrics( SM_CYSCREEN) - 110) / 2, 246, 110, 0, 0, hInst, NULL);
Patrice,
It is not a Win 8.1 10 sdk thing.
It fails with the VS 2012 express also.
James
I never had such problem when using AdjustWindowrRect, with 2010 (Pro), 2013 (Pro) , 2015 (Community),
and same with PB 9.05 and PB 10.04.
AdjustWindowRect has always served me well, since the first version of my WinLIFT skin engine long ago.
Quote
It fails with the VS 2012 express also.
Its working for you now with AdjustWindowRectEx() isn't it?
Patrice,
I did not mean your solution I meant my original failing code.
All works fine with both your's and José's solutions.
I am just trying to figure out why my original code fails and where the changes are documented. Fred said the original code worked fine with vs 2008. What changed and where is it documented?
James
Maybe Patrice or Jose know where its documented, and maybe not. I sure don't.
All the way back to my first coding of Windows with Windows 95 every new version of Windows changed the metrics of the various constituents of a window, e.g., the size of the title bar, the thickness of borders, etc. And it just came to my attention in fooling around with this that the numbers of the various equates passed into GetSystemMetrics() keeps growing as things become more complicated. I always figurred that if one needed to code an app at a high level of generalization (works on everything) one would have to make calls to all those GetSystemMetrics() values and code accordingly. I guess that's why in trying to understand this I tried to understand it through the GetSystemMetrics() function figuring that one could find the differences there. I couldn't quite piece it all together though for VS 2015. Maybe if I worked harder and longer at it. But if AdjustWindowRectEx() or AdjustWindowRect() takes care of it anyway, I'm not certain its worth the trouble. After all we're kind of talking about proprietary stuff internal to Windows here.
AdjustWindowRect calls AdjustWindowRectEx passing 0 as the extended style:
BOOL WINAPI DECLSPEC_HOTPATCH AdjustWindowRect ( LPRECT lpRect,
DWORD dwStyle,
BOOL bMenu
)
{
return AdjustWindowRectEx(lpRect, dwStyle, bMenu, 0);
}
AdjustWindowRextEx checks if the window is hooked and, if it is not, calls an internal function called RealAdjustWindowRectEx:
BOOL WINAPI DECLSPEC_HOTPATCH AdjustWindowRectEx ( LPRECT lpRect,
DWORD dwStyle,
BOOL bMenu,
DWORD dwExStyle
)
{
BOOL Hook, Ret = FALSE;
LoadUserApiHook();
Hook = BeginIfHookedUserApiHook();
/* Bypass SEH and go direct. */
if (!Hook) return RealAdjustWindowRectEx(lpRect, dwStyle, bMenu, dwExStyle);
_SEH2_TRY
{
Ret = guah.AdjustWindowRectEx(lpRect, dwStyle, bMenu, dwExStyle);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
}
_SEH2_END;
EndUserApiHook();
return Ret;
}
And this is the RealAdjustWindowRectEx function:
BOOL WINAPI RealAdjustWindowRectEx ( LPRECT lpRect,
DWORD dwStyle,
BOOL bMenu,
DWORD dwExStyle
)
{
SIZE BorderSize;
if (bMenu)
{
lpRect->top -= GetSystemMetrics(SM_CYMENU);
}
if ((dwStyle & WS_CAPTION) == WS_CAPTION)
{
if (dwExStyle & WS_EX_TOOLWINDOW)
lpRect->top -= GetSystemMetrics(SM_CYSMCAPTION);
else
lpRect->top -= GetSystemMetrics(SM_CYCAPTION);
}
UserGetWindowBorders(dwStyle, dwExStyle, &BorderSize, TRUE);
InflateRect(
lpRect,
BorderSize.cx,
BorderSize.cy);
return TRUE;
}
And the UserGetWindowBorders procedure called by RealAdjustWindowRectEx:
VOID UserGetWindowBorders ( DWORD Style,
DWORD ExStyle,
SIZE * Size,
BOOL WithClient
)
{
DWORD Border = 0;
if (UserHasWindowEdge(Style, ExStyle))
Border += 2;
else if (ExStyle & WS_EX_STATICEDGE)
Border += 1;
if ((ExStyle & WS_EX_CLIENTEDGE) && WithClient)
Border += 2;
if (Style & WS_CAPTION || ExStyle & WS_EX_DLGMODALFRAME)
Border ++;
Size->cx = Size->cy = Border;
if ((Style & WS_THICKFRAME) && !(Style & WS_MINIMIZE))
{
Size->cx += GetSystemMetrics(SM_CXFRAME) - GetSystemMetrics(SM_CXDLGFRAME);
Size->cy += GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYDLGFRAME);
}
Size->cx *= GetSystemMetrics(SM_CXBORDER);
Size->cy *= GetSystemMetrics(SM_CYBORDER);
}
What I don't know is if this code is still being used as is by the newest Windows versions or if they have changed it.
Because I was tired of seeing the size of the client window to change with each version of Windows, because of different sizes of gui elements such the caption, I wrote the function AfxSetWindowClientSize that is a wraper for AdjustWindowRectEx, but also taking into account possible menu wrapping and the WS_VSCROLL and WS_HSCROLL styles. I borrowed the code for menu wrapping from somewhere.
José,
This PbWin10 SDK code does not truncate the window on my Win10 64 machine.
'======================================================================
' Declares
'----------------------------------------------------------------------
#COMPILE EXE
'----------------------------------------------------------------------
#IF %PB_REVISION < &H1000 ' if compiler PBWIN9 or earlier
%USEMACROS = 1
#ENDIF
#INCLUDE "WIN32API.INC"
'----------------------------------------------------------------------
%IDC_EDIT100 = 100
'======================================================================
FUNCTION WINMAIN (BYVAL hInst AS DWORD, BYVAL hPrevInstance AS DWORD, _
BYVAL lpszCmdLine AS ASCIIZ PTR, BYVAL nCmdShow AS LONG) AS LONG
'----------------------------------------------------------------------
' Program entrance
'----------------------------------------------------------------------
LOCAL hDlg AS DWORD, hCtl AS DWORD, hFont AS DWORD, _
sBuf AS STRING, wc AS WndClassEx, szClassName AS ASCIIZ * 80
hFont = GetStockObject(%DEFAULT_GUI_FONT)
szClassName = "MyClassName"
wc.cbSize = SIZEOF(wc)
wc.style = %CS_HREDRAW OR %CS_VREDRAW
wc.lpfnWndProc = CODEPTR(WndProc)
wc.cbClsExtra = 0
wc.cbWndExtra = 0
wc.hInstance = hInst
wc.hIcon = LoadIcon (%NULL, BYVAL %IDI_APPLICATION)
wc.hCursor = LoadCursor(%NULL, BYVAL %IDC_ARROW)
wc.hbrBackground = %COLOR_3DFACE + 1
wc.lpszMenuName = %NULL
wc.lpszClassName = VARPTR(szClassName)
wc.hIconSm = LoadIcon (%NULL, BYVAL %IDI_APPLICATION)
CALL RegisterClassEx (wc)
hDlg = CreateWindowEx(%WS_EX_DLGMODALFRAME OR %WS_EX_CONTROLPARENT OR %WS_EX_WINDOWEDGE, szClassName, "What is your name?", _
%WS_POPUP OR %WS_VISIBLE OR %WS_CLIPSIBLINGS OR %WS_CAPTION OR _
%DS_3DLOOK OR %DS_NOFAILCREATE OR %DS_SETFONT OR %DS_MODALFRAME OR _
%DS_CENTER, _
(GetSystemMetrics(%SM_CXSCREEN) - 246) / 2, _
(GetSystemMetrics(%SM_CYSCREEN) - 110) / 2, _
246, 110, 0, 0, GetModuleHandle(""), BYVAL %NULL)
hCtl = CreateWindowEx(%WS_EX_CLIENTEDGE, "Edit", BYVAL %NULL, _
%WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP OR %ES_AUTOHSCROLL, _
21, 20, 201, 19, _
hDlg, %IDC_EDIT100, GetModuleHandle(""), BYVAL %NULL)
IF hFont THEN SendMessage hCtl, %WM_SETFONT, hFont, 0
hCtl = CreateWindowEx(0, "Button", "OK", _
%WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP, _
51, 52, 60, 23, _
hDlg, %IDOK, GetModuleHandle(""), BYVAL %NULL)
IF hFont THEN SendMessage hCtl, %WM_SETFONT, hFont, 0
hCtl = CreateWindowEx(0, "Button", "Cancel", _
%WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP, _
126, 52, 60, 23, _
hDlg, %IDCANCEL, GetModuleHandle(""), BYVAL %NULL)
IF hFont THEN SendMessage hCtl, %WM_SETFONT, hFont, 0
ShowWindow hDlg, nCmdShow
UpdateWindow hDlg
LOCAL Msg AS tagMsg
WHILE GetMessage(Msg, %NULL, 0, 0)
TranslateMessage Msg
DispatchMessage Msg
WEND
FUNCTION = msg.wParam
END FUNCTION
'======================================================================
FUNCTION WndProc (BYVAL hWnd AS DWORD, BYVAL wMsg AS DWORD, _
BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG
'----------------------------------------------------------------------
' Main Window procedure
'----------------------------------------------------------------------
SELECT CASE wMsg
CASE %WM_CREATE
'A good place to initiate things, declare variables,
'create controls and read/set settings from a file, etc.
'-------------------------------------------------------
CASE %WM_COMMAND
'Messages from controls and menu items are handled here.
'-------------------------------------------------------
SELECT CASE LOWRD(wParam)
CASE %IDCANCEL ' <- Esc key also triggers %IDCANCEL
IF HIWRD(wParam) = %BN_CLICKED OR HIWRD(wParam) = 1 THEN
SendMessage hWnd, %WM_DESTROY, wParam, lParam
FUNCTION = 0 : EXIT FUNCTION
END IF
CASE %IDOK ' <- Enter key usually triggers %IDOK
IF HIWRD(wParam) = %BN_CLICKED OR HIWRD(wParam) = 1 THEN
' do whatever...
END IF
CASE %IDC_EDIT100
SELECT CASE HIWRD(wParam)
CASE %EN_UPDATE ' Text is about to be changed
CASE %EN_CHANGE ' Text has changed
CASE %EN_SETFOCUS ' Control is getting focus
CASE %EN_KILLFOCUS ' Control is losing focus
END SELECT
END SELECT
CASE %WM_CTLCOLORBTN, %WM_CTLCOLOREDIT, %WM_CTLCOLORLISTBOX, %WM_CTLCOLORSTATIC
' wParam is handle of control's display context (hDC)
' lParam is handle of control
' Example on how to set colors to a specific control:
'-----------------------------------------------------
'IF lParam = GetDlgItem(hWnd, CtlId) THEN
' SetBkColor wParam, GetSysColor(%COLOR_INFOBK)
' SetTextColor wParam, GetSysColor(%COLOR_INFOTEXT)
' FUNCTION = GetSysColorBrush(%COLOR_INFOBK)
' EXIT FUNCTION
'END IF
'-----------------------------------------------------
CASE %WM_DESTROY
' is sent when program ends - a good place to delete any created objects and
' store settings in file for next run, etc. Must send PostQuitMessage to end
' properly in SDK-style dialogs. The PostQuitMessage function sends a WM_QUIT
' message to the program's (thread's) message queue, and then WM_QUIT causes
' the GetMessage function to return zero in WINMAIN's message loop.
'----------------------------------------------------------------------------
PostQuitMessage 0
FUNCTION = 0 : EXIT FUNCTION
CASE %WM_SIZE
IF wParam <> %SIZE_MINIMIZED THEN
' LOWRD(lParam) = width of dialog's client area
' HIWRD(lParam) = height of dialog's client area
' Example that resizes a control to dialog's client area:
'------------------------------------------------------
'SetWindowPos GetDlgItem(hWnd, CtlId), 0, 0, 0, _
' LOWRD(lParam), HIWRD(lParam), _
' %SWP_NOMOVE OR %SWP_NOZORDER
END IF
END SELECT
FUNCTION = DefWindowProc(hWnd, wMsg, wParam, lParam)
END FUNCTION
No other compiler I have tried:
PbWin9
PbWin10
PbCC6
PbCC5
NUWEN GCC Distro
TDM-GCC Distro
PellesC 8
Tiny C 9.26
truncates the window except Visual Studio 2012 Express and Visual Studio 2015 Community.
James
Scrollbars must be part of the client area (they are drawn directly by the OS itself) except with a Skin engine.
Also the scrollbars could be turned on or off, or only the vertical or the horizontal could be visible.
Or they can be enabled only after the window has been shown, based on what should be shown in it.
Think of a POPUP LISTBOX with the WS_EX_TOOLWINDOW StyleEx and any combination of WS_VSCROLL and/or WS_HSCROLL, like with my zTrace utility.
...
And what happens if you want that the visible content won't be reduced by the scrollbars? If you don't care, then call AdjustWindowRextEx directly.
Ok gentlemen you have convinced me: "resistance is futile" :)
I tried adding WS_SCROLL | WS_HSCROLL to the style and of course it was as you said with all compilers.
I was worried my port of José's code would not work with the other compilers but all is well.
One item I feel should be in all c++ developers toolbox is the NUWEN distro:
https://nuwen.net/mingw.html
Take a check of his web site.
But the still unanswered question is when did the change take place with VS and where is it documented?
James
For me the best window style is... WS_POPUP, without anything else, then you can take the control of your window just as you want.
Remember this old thread
http://www.jose.it-berater.org/smfforum/index.php?topic=1164.0
8)
Quote from: Patrice Terrier on November 21, 2016, 12:11:36 PM
Scrollbars must be part of the client area (they are drawn directly by the OS itself) ...
Strictly speaking I'm afraid this isn't exactly so, Patrice.
Windows
native scrollbars, disabled or enabled alike, that it adds in response to the WS_VSCROLL/WS_HSCROLL window styles, as opposed to their common or user control varieties,
are not part of the window client area once they pop up automatically into view at app start or become visible through programmatic methods at some later point in time:
- they do not respond to Windows hit test with a HTCLIENT flag but rather with HTVSCROLL and HTHSCROLL flags, respectively;
- window client area as reported by the GetClientArea() API is reduced by the scrollbar sizes once they become visible; and
- the user should utilize the GetWindowDC() accessor rather than GetDC() if they'd like to draw in the window area occupied by either scrollbar; in other words, any native scrollbar a window has is clipped out of its client area.
Contrary to that, the Common Controls' Flat Scroll Bar or custom user control scrollbars would be hosted in the window client area just like any other ordinary static or button or whatever controls and
would not affect the window (non-)client area sizes, accessors or hit tests, and their underlying area would still be reported as HTCLIENT area. Of course, if you wouldn't fool the OS with your custom zzTop() hit testing in the meantime. :)
Here is how i check in WinLIFT (in polling mode) if the scrollbars are visible or not.
(Using a combination of GetWindowRect and GetClientRect)
long skCheckScrollBar (IN HWND hWnd) {
// Check if the control's scrollbars are shown.
// Returns zero if (none, 1 if vertical, 2 if horizontal and 3 if both.
RECT rw = {0}, rc = {0};
long nRet = SCROLLBAR_NONE;
if (IsWindowVisible(hWnd)) { // 06-25-2015
GetWindowRect(hWnd, &rw);
GetClientRect(hWnd, &rc);
if (rc.right <= rw.right - rw.left - GetSystemMetrics(SM_CXVSCROLL)) { nRet = SCROLLBAR_VERT; }
if (rc.bottom <= rw.bottom - rw.top - GetSystemMetrics(SM_CXHSCROLL)) { nRet = nRet + SCROLLBAR_HORZ; }
}
return nRet;
}
and for more oddities play with: menu, toolbar, statusbar, rebar and the like.
It looks like you've opened up a can of worms Jim! :)
QuoteIt looks like you've opened up a can of worms Jim
And i have a couple more, when dealing with Windows 8+ 10, and moving unexpectedly a window outside of the bound edges of the desktop.
You must absolutly handle properly these messages:
case %WM_GETMINMAXINFO
pMM = lParam
@pMM.ptMinTrackSize.x = gP.MinTrackSizeW
@pMM.ptMinTrackSize.y = gP.MinTrackSizeH
function = 0: exit function
case %WM_MOVING '// Keep the window into the working area
hMonitor = MonitorFromWindow(hWnd, %MONITOR_DEFAULTTONEAREST)
lpr = lParam
if (@lpr.nTop < 0) then @lpr.nTop = 0
@lpr.nRight = @lpr.nLeft + gP.MinTrackSizeW
@lpr.nBottom = @lpr.nTop + gP.MinTrackSizeH
tmi.cbSize = sizeof(tmi)
if (GetMonitorInfo(hMonitor, tmi)) then
if (@lpr.nBottom > tmi.rcWork.nBottom) then
@lpr.nBottom = tmi.rcWork.nBottom
@lpr.nTop = @lpr.nBottom - gP.MinTrackSizeH
end if
end if
function = %TRUE: exit function
Or Windows will resize your window based on its own rules despite of the minimum client size you specify, terrible when using a non-client area that doesn't use the Windows default theme.
Quote from: Patrice Terrier on November 22, 2016, 10:06:06 AMmore oddities play with: menu, toolbar, statusbar, rebar and the like.
These, except for the menu that is a genuine non-client HTMENU area of the window, are all HTCLIENT area controls that don't affect the rectangle returned by a GetClientArea() call. In other words, they behave like ordinary labels, buttons, checkboxes, progressbars, etc. that you would put into the window client area to enable user interaction in accordance with your app's intended purposes. It's just another story that they are normally auto-resizable and would snap in their places all by themselves seamlessly unless given visible border styles of their own to discern them from the adjacent non-client spaces at a glance.
Quote from: Patrice Terrier on November 22, 2016, 03:57:01 PMAnd i have a couple more, when dealing with Windows 8+ 10 ...
You'd have
a lot more than that had you tried to skin windows that belong to a process other than your own -- something such SW as e.g. Stardock Corp.'s WindowBlinds does. :)
Mike--
A skin engine is not intendend to skin the whole Windows UI, but only a specific window.
And you can use simultaneously different skins with specific applications.
On the other hand WindowBlinds is a Theme engine that substitutes its own graphic components in lieu of those being used by Windows, meaning ALL the windows have the same look (except those skinned).
Theming is {easier to program} than Skinning, because you just have to replace the default Windows theme components.
However there is much more graphic art work to do with theming to create all the bitmaps needed, including icons and a specific wallpapers background set. Doable only if you have a team of talented artist to concieve new themes.
Note: with Theming, all the GetSystemMetrics functions are matching those used by the theme.
Quote from: Patrice Terrier on November 23, 2016, 11:56:00 AMA skin engine is not intendend to skin the whole Windows UI, but only a specific window.
Contemporary Eclecta (FBSL's official IDE application) is a very heavily skinned "specific window". Skinning (in your sense of the word) helps me keep the user environment consistent across the platforms and is also a tribute to XP that was the greatest OS I ever used.
Eclecta also happens to utilize
a lot of owned child processes as its tools and utilities, and it would've been unnatural should all of them keep on using their own skins and colors, even if beautifully themed by a mature OS which of course can hardly be expected of such a graphical and metrical nuisance as Windows 10.
I guess Eclecta's skinning lies on the borderline between what you call themeing and skinning "a specific window" proper.
In fact the 64-bit Unicode child window chaos in Win 10's Calculator and Task Monitor was the only real reason why I would install Win 10 Pro Anniversary Edition at all. :)
Interresting threads about the same problem
http://stackoverflow.com/questions/11783086/get-exact-window-region-size-createwindow-window-size-isnt-correct-size-of-wi
http://stackoverflow.com/questions/27928254/adjustwindowrectex-and-getwindowrect-give-wrong-size-with-ws-overlapped
When in aero mode, see also the DwmGetWindowAttribute (https://msdn.microsoft.com/en-us/library/windows/desktop/aa969515(v=vs.85).aspx) function
Windows 10 has thin invisible borders on left, right, and bottom, it is used to grip the mouse for resizing.
The borders might look like this: 7,0,7,7 (left, top, right, bottom)
http://stackoverflow.com/questions/34139450/getwindowrect-returns-a-size-including-invisible-borders
Rather than GetWindowRect, use this API to retrieve the correct size in DWM AERO mode
RECT lpr = { 0 };
HRESULT hRes = DwmGetWindowAttribute(hWin, DWMWA_EXTENDED_FRAME_BOUNDS, &lpr, sizeof(RECT));
And to detect if DWM is enabled, here is what i am doing in WinLIFT
long zDwmIsCompositionEnabled () {
long nRet = 0;
HMODULE hLib = LoadDWM();
if (hLib) {
long_proc (BOOL*);
zProc hProc = (zProc) GetProcAddress(hLib, "DwmIsCompositionEnabled");
if (hProc) {
BOOL bAero = FALSE;
if (hProc(&bAero) == 0) {
if (bAero) { nRet = -1; }
}
}
}
return nRet;
}
Quote from: Patrice Terrier on November 24, 2016, 09:56:47 AMWindows 10 has thin invisible borders on left, right, and bottom, it is used to grip the mouse for resizing.
The borders might look like this: 7,0,7,7 (left, top, right, bottom)
http://stackoverflow.com/questions/34139450/getwindowrect-returns-a-size-including-invisible-borders
Hehe, thanks for this link Patrice!
It actually points to an answer (at the very bottom of the page) that contains yet another link:
http://winaero.com/blog/enable-the-hidden-aero-lite-theme-in-windows-10/which teaches us how to
non-intrusively enable Windows 10 unofficial Aero Lite theme that would make those invisible border extras fully visible and that would also bring the GetWindowRect() returns back to their expected visually consistent values! :)
For all intents and purposes, Aero Lite is roughly equivalent to Basic-style non-Aero theming in Vista and 7.
Addendum to the above link:Once you double-click the new AeroLite.theme file on your desktop to install the new Basic-like theme in your system, you may safely delete this file from your desktop altogether. The new theme will be installed permanently alongside the other themes available via standard Personalization->Theming functionality.
About the transparent border section, i am using exactly the same concept when running WinLIFT with a composited Skin.
it is based on the use of transparent png components, and this allows me to create shadow or Hallo effects.
This is clearly visible on this WinLIFT screenshot
(http://www.zapsolution.com/pictures/wlftwdm.jpg)
This is the reason why i have always considered the client size when skinning a window rather than its WindowRect, because i knew that it is changing from one OS to another.
Since VISTA i have always used DWM, and the Device Window Manager API allows me to create any Skin i could imagine with full variable opacity, as you can see on the caption of the screenshot.
WinLIFT has its own skGetSystemMetrics API to retrieve the correct size of its non-client parts.
And notice the use of SK_DWM_LEFT, SK_DWM_TOP, SK_DWM_RIGHT, SK_DWM_BOTTOM.
long skGetSystemMetrics (IN long nMeasure) { // dllexport
if ((nMeasure > -1) || (nMeasure < SKMETRIC_UB)) {
long xWidth, yHeight, CyCaption, CxFrameLeft, CxFrameRight, CyFrameBottom, CxFrame, CyMenu;
switch (nMeasure) {
case SK_CYCAPTION:
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"CYCAPTION"));
if (g_nMetrics[nMeasure] == 0) {
skGetBitmapSize(g_CaptionM, xWidth, CyCaption);
g_nMetrics[nMeasure] = CyCaption;
}
}
break;
case SK_CYMENU:
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
skGetBitmapSize(g_MenuBar, xWidth, CyMenu);
g_nMetrics[nMeasure] = CyMenu;
}
break;
case SK_CYSTATUS:
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = 0;
if (CheckStyle(skWindowStyle(0), SK_STATUSBAR)) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"CYSTATUS"));
}
}
break;
case SK_CXFRAMELEFT:
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"CXFRAMELEFT"));
if (g_nMetrics[nMeasure] == 0) {
skGetBitmapSize(g_SideL, CxFrameLeft, yHeight);
g_nMetrics[nMeasure] = CxFrameLeft;
}
}
break;
case SK_CXFRAMERIGHT:
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"CXFRAMERIGHT"));
if (g_nMetrics[nMeasure] == 0) {
skGetBitmapSize(g_SideR, CxFrameRight, yHeight);
g_nMetrics[nMeasure] = CxFrameRight;
}
}
break;
case SK_CYFRAMEBOTTOM:
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"CYFRAMEBOTTOM"));
if (g_nMetrics[nMeasure] == 0) {
skGetBitmapSize(g_BottomM, xWidth, CyFrameBottom);
g_nMetrics[nMeasure] = CyFrameBottom;
}
}
break;
case SK_CXSIZE: // Dimension of caption button in pixels
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"CXSIZE"));
}
case SK_CYSIZE: // Dimension of caption button in pixels
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"CYSIZE"));
}
break;
case SK_CXSYSBUT: // Right X seed for system buttons
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"CXSYSBUT"));
if (g_nMetrics[nMeasure] == 0) {
skGetBitmapSize(g_SideL, CxFrame, yHeight);
g_nMetrics[nMeasure] = CxFrame;
if (g_nMetrics[nMeasure] == 0) { g_nMetrics[nMeasure] = 6; }
}
}
break;
case SK_CYSYSBUT: // Right Y seed for system buttons
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"CYSYSBUT"));
}
break;
case SK_CXCAPTEXT: // X coordinate for Caption text
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"CXCAPTEXT"));
}
break;
case SK_CYCAPTEXT: // Y coordinate for Caption text
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"CYCAPTEXT"));
}
break;
case SK_XBUT3DBORDER: // Horizontal non stretched button border
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"XBUT3DBORDER"));
}
break;
case SK_YBUT3DBORDER: // Vertical non stretched button border
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"YBUT3DBORDER"));
}
break;
case SK_CXSYSICON: // X seed for system icon
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"CXSYSICON"));
}
break;
case SK_CYSYSICON: // Y seed for system icon
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"CYSYSICON"));
}
break;
case SK_CXSYSLED: // X coordinate for Caption led
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"CXSYSLED"));
}
break;
case SK_CYSYSLED: // Y coordinate for Caption led
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"CYSYSLED"));
}
break;
case SK_TRANSLUCENCY: // Translucency
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"TRANSLUCENCY"));
if (g_nMetrics[nMeasure] == 0) { g_nMetrics[nMeasure] = 50; }
}
break;
case SK_MENUTRANSLUCENCY: // Menu translucency percentage
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"MENUTRANSLUCENCY"));
if (g_nMetrics[nMeasure] == 0) { g_nMetrics[nMeasure] = 100; }
}
break;
case SK_PAINT_BORDER:
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"PAINT_BORDER"));
}
break;
case SK_PAINT_BACKGROUND:
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"PAINT_BACKGROUND"));
}
break;
case SK_ICONSIZE:
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = max(LongVAL(skLoadString(L"ICONSIZE")), 16);
}
break;
case SK_OUTER_GLOW:
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"OUTER_GLOW"));
}
break;
case SK_SKIN_ICON:
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"SKIN_ICON"));
}
break;
case SK_DWM_AERO:
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"DWM_AERO"));
if (zDwmIsCompositionEnabled() == 0) { g_nMetrics[nMeasure] = 0; }
}
break;
case SK_DWM_LEFT:
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"DWM_LEFT"));
}
break;
case SK_DWM_TOP:
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"DWM_TOP"));
}
break;
case SK_DWM_RIGHT:
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"DWM_RIGHT"));
}
break;
case SK_DWM_BOTTOM:
if (g_nMetrics[nMeasure] == MESURE_UNDEFINED) {
g_nMetrics[nMeasure] = LongVAL(skLoadString(L"DWM_BOTTOM"));
}
break;
}
return g_nMetrics[nMeasure]; }
else {
return 0;
}
}
Patrice,
I told you more than once that for over a decade now (which is effectively forever) I've been, and still am, a dedicated admirer of your beautiful GUIs. :)
It's just that I can't go the same route for fear lest you might think I'm reverse-engineering your GDImage.dll. ;D
This is exactly why my skinning engine in fact suppresses entirely any Windows system drawing in the non-client area (Classic, Basic and Aero alike), reassigns to it a new window region of known skin-dependent size, intersects it with the window's existing client area region, and draws in this new non-client region fixing the hit-test flags as necessary to match my custom sizes and placement of caption buttons and system icon, as well as the size of caption and menu and the size, placement and spaces between the menu buttons, SDI and MDI. Naturally both the system and main menu panes are entirely owner-drawn as well.
Frankly, it takes so much effort that I've never been physically able to go beyond rebars, toolbars and statusbars and get down to simpler targets like buttons, checkboxes, option buttons, progressbars, and especially scrollbars. :)