Say, I have a Win32 app with a menu added to the main window via CreateWindowEx method, and I need to know its usable width. The best way to illustrate the width I'm asking for is with this diagram:
So how do I calculate it?
When I try to do the following, it gives me the width of the client area:
MENUBARINFO mbi = {0};
mbi.cbSize = sizeof(mbi);
if(::GetMenuBarInfo(hWnd, OBJID_MENU, 0, &mbi))
{
int nUsableWifth = mbi.rcBar.right - mbi.rcBar.left;
}
If you want to know the width that is used for menu items, you can either:
use GetMenuItemRect() to get the screen coordinates of the last menu item, and then convert them to client coordinates within the parent window. The converted right edge coordinate will give you the width:
HMENU hMenu = ::GetMenu(hWnd);
int count = ::GetMenuItemCount(hMenu);
RECT r;
if (::GetMenuItemRect(hWnd, hMenu, count-1, &r))
{
::MapWindowPoints(NULL, hWnd, (LPPOINT)&r, 2);
int nUsedWidth = r.right;
...
}
the above assumes the menu starts at offset 0 within the window's client area. If you don't want to rely on that, you could instead get the screen coordinates of the 1st menu item and subtract it from the right-edge screen coordinate of the last menu item:
HMENU hMenu = ::GetMenu(hWnd);
int count = ::GetMenuItemCount(hMenu);
RECT rFirst, rLast;
if (::GetMenuItemRect(hWnd, hMenu, 0, &rFirst) &&
::GetMenuItemRect(hWnd, hMenu, count-1, &rLast))
{
int nUsedWidth = rLast.right - rFirst.left;
...
}
Either way, if you then want to know the width that is not used for menu items, simply get the menu's total width and subtract the above calculated width:
MENUBARINFO mbi = {0};
mbi.cbSize = sizeof(mbi);
if (::GetMenuBarInfo(hWnd, OBJID_MENU, 0, &mbi))
{
int nUsableWidth = (mbi.rcBar.right - mbi.rcBar.left) - nUsedWidth;
...
}
UPDATE: I didn't realize a window's menu can wrap its items vertically if the client area is too small to display them all on one line. In that case, you might have to do something more like this instead to calculate nUsedWidth:
HMENU hMenu = ::GetMenu(hWnd);
int count = ::GetMenuItemCount(hMenu);
int nUsedWidth = 0;
RECT r;
for(int idx = 0; idx < count; ++idx)
{
if (::GetMenuItemRect(hWnd, hMenu, idx, &r))
{
::MapWindowPoints(NULL, hWnd, (LPPOINT)&r, 2);
if (r.right > nUsedWidth)
nUsedWidth = r.right;
}
}
...
Or:
HMENU hMenu = ::GetMenu(hWnd);
int count = ::GetMenuItemCount(hMenu);
int nUsedWidth = 0;
RECT rFirst, r;
if (::GetMenuItemRect(hWnd, hMenu, 0, &rFirst))
{
nUsedWidth = rFirst.right - rFirst.left;
for (int idx = 1; idx < count; ++idx)
{
if (::GetMenuItemRect(hWnd, hMenu, idx, &r))
{
int nWidth = r.right - rFirst.left;
if (nWidth > nUsedWidth)
nUsedWidth = nWidth;
}
}
}
...
Related
I am trying to create a custom title bar on win32 application. I handled WM_NCCLACSIZE and removed system title bar
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
case WM_NCCALCSIZE:
{
// get default NC size
auto params = reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam);
const auto originalTop = params->rgrc[0].top;
const auto originalSize = params->rgrc[0];
const auto ret = DefWindowProc(hWnd, WM_NCCALCSIZE, wParam, lParam);
auto newSize = params->rgrc[0];
// remove title bar
newSize.top = originalTop;
if (IsZoomed(hWnd)) {
newSize.top += 8;
newSize.bottom -= 2;
}
params->rgrc[0] = newSize;
return 0;
}
Then I draw title bar and min, max, close button in my client area.
I read the document and I would like to show layout menu like native window.
So I handled WM_NCHITTEST and returned HTREDUCE HTZOOM HTCLOSE instead of handling click event and ShowWindow(hwnd, SW_SHOWMAXIMIZED).
But when I click the area, there is ugly system buttons in my title bar.
case WM_NCHITTEST:
{
auto x = GET_X_LPARAM(lParam);
auto y = GET_Y_LPARAM(lParam);
RECT rect;
GetWindowRect(hWnd, &rect);
if (y - rect.top < 40) {
auto width = rect.right - rect.left;
RECT minbtn = { width - 3 * 120 ,0, width - 2 * 120 ,40 };
RECT maxbtn = { width - 2 * 120 ,0, width - 1 * 120 ,40 };
RECT closebtn = { width - 1 * 120 ,0, width - 0 * 120 ,40 };
if (minbtn.left + rect.left < x && minbtn.right + rect.left > x)
return HTREDUCE;
if (maxbtn.left + rect.left < x && maxbtn.right + rect.left > x)
return HTZOOM;
if (closebtn.left + rect.left < x && closebtn.right + rect.left > x)
return HTCLOSE;
return HTCAPTION;
}
return DefWindowProc(hWnd, WM_NCHITTEST, wParam, lParam);;
}
What should I do?
My application supports per-monitor DPI-awareness version 2. I have two monitors - one scaled at 100% and the other at 125%. When moving my application's window to the monitor with DPI scaling and setting the new size using the recommended size given in the WM_DPICHANGED message, the resulting client area size is a few pixels bigger than it should be.
For example, the window's client area size in my case is 300x200 pixels. On the monitor with 125% scaling, the scaling factor is 1.25, so the resulting client area size should be 375x250. When I set the window size using the recommended one received in the WM_DPICHANGED message, the resulting client area size is 377x252. Windows documentation claims scaling to be linear, yet it fails to work like that.
Minimal example:
#include <Windows.h>
void set_window_size(HWND window, DWORD window_style, int width, int height)
{
UINT dpi = GetDpiForWindow(window);
float scaling_factor = static_cast<float>(dpi) / USER_DEFAULT_SCREEN_DPI;
RECT scaled_size;
scaled_size.left = 0;
scaled_size.top = 0;
scaled_size.right = static_cast<LONG>(width * scaling_factor);
scaled_size.bottom = static_cast<LONG>(height * scaling_factor);
// Adjust the size to account for non-client area
AdjustWindowRectExForDpi(&scaled_size, window_style, false, 0, dpi);
SetWindowPos(window, nullptr, 0, 0, scaled_size.right - scaled_size.left, scaled_size.bottom - scaled_size.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE));
}
// These sizes are for the client area
constexpr auto window_width = 300;
constexpr auto window_height = 200;
constexpr auto window_class_name = L"startup_dialog";
constexpr auto window_style = WS_OVERLAPPEDWINDOW;
LRESULT CALLBACK window_procedure(HWND window, UINT message, WPARAM w_param, LPARAM l_param)
{
switch (message)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
case WM_DPICHANGED:
{
RECT* rect = reinterpret_cast<RECT*>(l_param);
SetWindowPos(window, nullptr, rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top, SWP_NOZORDER | SWP_NOACTIVATE);
}
}
return DefWindowProcW(window, message, w_param, l_param);
}
int CALLBACK wWinMain(HINSTANCE instance, HINSTANCE prev_instance, PWSTR cmd_line, int cmd_show)
{
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
WNDCLASSEXW window_class;
window_class.cbSize = sizeof(window_class);
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.lpfnWndProc = window_procedure;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hInstance = instance;
window_class.hIcon = nullptr;
window_class.hCursor = nullptr;
window_class.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
window_class.lpszMenuName = nullptr;
window_class.lpszClassName = window_class_name;
window_class.hIconSm = nullptr;
RegisterClassExW(&window_class));
HWND window = CreateWindowExW(0, window_class_name, L"Example", window_style, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, nullptr, nullptr, instance, nullptr);
// Set the initial DPI-scaled window size
set_window_size(window, window_style, window_width, window_height);
ShowWindow(window, SW_SHOWNORMAL);
// Message loop
MSG message;
int result;
while ((result = GetMessageW(&message, nullptr, 0, 0)) != 0)
{
if (result == -1)
{
return 1;
}
else
{
TranslateMessage(&message);
DispatchMessageW(&message);
}
}
return static_cast<int>(message.wParam);
}
Error checking is removed for brevity.
The example requires Windows 10 SDK 14393+ to compile and Windows 10 1607+ to run.
How do I fix the incorrect recommended window size given in the WM_DPICHANGED message?
The problem occurs due to a Windows bug, which causes the new window size to be incorrectly calculated. The bug can be worked around by handling the WM_GETDPISCALEDSIZE message and calculating the new window size yourself.
Example of handling the message based on the question's example:
case WM_GETDPISCALEDSIZE:
{
UINT dpi = static_cast<UINT>(w_param);
float scaling_factor = static_cast<float>(dpi) / USER_DEFAULT_SCREEN_DPI;
RECT client_area;
client_area.right *= scaling_factor;
client_area.bottom *= scaling_factor;
RECT window_rectangle;
window_rectangle.left = 0;
window_rectangle.top = 0;
window_rectangle.right = static_cast<LONG>(window_width * scaling_factor);
window_rectangle.bottom = static_cast<LONG>(window_height * scaling_factor);
if (!AdjustWindowRectExForDpi(&window_rectangle, window_style, false, 0, dpi))
{
// Error handling
return 0;
}
SIZE* new_size = reinterpret_cast<SIZE*>(l_param);
new_size->cx = window_rectangle.right - window_rectangle.left;
new_size->cy = window_rectangle.bottom - window_rectangle.top;
return 1;
}
Note that with this approach you must make the window_width and window_height variables available in the message. In the question's example this was done using constexpr global variables.
Alternative approach, which scales based on the previous client area size, but is likely slightly slower:
case WM_GETDPISCALEDSIZE:
{
UINT dpi = static_cast<UINT>(w_param);
float scaling_factor = static_cast<float>(dpi) / USER_DEFAULT_SCREEN_DPI;
RECT client_area;
if (!GetClientRect(window, &client_area))
{
// Error handling
return 0;
}
client_area.right = static_cast<LONG>(client_area.right * scaling_factor);
client_area.bottom = static_cast<LONG>(client_area.bottom * scaling_factor);
if (!AdjustWindowRectExForDpi(&client_area, window_style, false, 0, dpi))
{
// Error handling
return 0;
}
SIZE* new_size = reinterpret_cast<SIZE*>(l_param);
new_size->cx = client_area.right - client_area.left;
new_size->cy = client_area.bottom - client_area.top;
return 1;
}
Also worth noting that there seems to be another bug, where if you breakpoint in either of the above-shown messages, the recommended rectangle passed to WM_DPICHANGED will be incorrect.
I encountered a problem scrolling large areas in a CScrollView. When slowly moving the scrollbar from top to bottom the behaviour is the following: At first the scrolling is working fine. At some point scrolling further does not do anything, instead the top of the area is shown. At some point scrolling starts again from the top.
Here is a small example. I created a new MFC project using the document-view-model and used a CScrollView as the view class. I added the following code to create a large area and add some text to show, which part is currently shown:
void CScrollViewTest2View::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
CSize sizeTotal;
// TODO: calculate the total size of this view
sizeTotal.cx = sizeTotal.cy = 100*1000;
SetScrollSizes(MM_TEXT, sizeTotal);
for(int i = 0; i < 1000; i++)
{
CStatic* label = new CStatic();
label->Create(NULL, WS_CHILD | WS_VISIBLE, CRect(10,10 + i*100,100,30 + i*100), this);
CString text;
text.Format(L"%d",i);
label->SetWindowText(text);
}
}
If I add the following code I see that during the scrolling the 'nPos' value seems to wrap around. This would explain the behaviour. But I don't know how to work around that.
BOOL CScrollViewTest2View::OnScroll(UINT nScrollCode, UINT nPos, BOOL bDoScroll )
{
CString msg;
msg.Format(L"nPos = %u\n",nPos);
TRACE(msg);
return CScrollView::OnScroll(nScrollCode, nPos, bDoScroll);
}
and here is the output when it stops scrolling:
nPos = 27826
nPos = 29190
nPos = 30281
nPos = 31372
nPos = 31645
nPos = 32464
nPos = 4294938588
nPos = 4294939134
nPos = 4294939407
nPos = 4294939680
nPos = 4294940225
nPos = 4294940771
So is there a way to use a CScrollView to completely scroll down a large area?
The code of the sample project can be found here.
The static controls are not placed where you expect them to be. To see the problem, run the following code:
static CStatic test;
CRect r(0, 0, 100, 30);
r.MoveToY(40000);
test.Create(0, WS_CHILD | WS_VISIBLE, r, this);
test.GetWindowRect(r);
TRACE("%d\n", r.top);
r.top should be 32767. This is because of 16bit limits in Windows. All of the controls whose x/y position exceed this limit, are pushed back to this position.
OnScroll function suffers from a similar problem, however this can be fixed by using GetScrollInfo
Add ON_WM_VSCROLL to message map and the following OnVScroll override to your class
void CScrollViewTest2View::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CView::OnVScroll(nSBCode, nPos, pScrollBar);
SCROLLINFO info;
GetScrollInfo(SB_VERT, &info, SIF_ALL);
int pos = info.nPos;
int save = pos;
switch (nSBCode)
{
case SB_LEFT: pos = info.nMin; break;
case SB_RIGHT: pos = info.nMax; break;
case SB_LINELEFT: pos--; break;
case SB_LINERIGHT: pos++; break;
case SB_PAGELEFT: pos -= info.nPage; break;
case SB_PAGERIGHT: pos += info.nPage; break;
case SB_THUMBPOSITION: pos = info.nTrackPos; break;
case SB_THUMBTRACK: pos = info.nTrackPos; break;
}
//make sure the new position is within range
if (pos < info.nMin) pos = info.nMin;
int max = info.nMax - info.nPage + 1;
if (pos > max) pos = max;
OnScrollBy(CSize(0, pos - save), 1);
//EDIT: moved this line after OnScrollBy and added condition
if (info.nPos != pos)
{
info.nPos = pos;
SetScrollInfo(SB_VERT, &info, FALSE);
}
UpdateWindow();
}
This will not solve the problem with windows controls whose x/y position is greater than 32,000, but at least it will scroll the DC as expected.
For testing, remove the static controls. You can use the draw function below to test the painting:
void CScrollViewTest2View::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
CSize sizeTotal(0, 100 * 1000);
SetScrollSizes(MM_TEXT, sizeTotal);
}
void CScrollViewTest2View::OnDraw(CDC* pDC)
{
for (int i = 0; i < 1000; i++)
{
int y = i * 100;
CString s;
s.Format(L"%d ", y);
pDC->TextOutW(200, y, s);
}
}
I know that there is a lot questions about how to set console size. But all found solutions are the same to my and my code doesn't works for me.
Ok, so for setting console window size, I need two functions. They are SetConsoleScreenBufferSize() and SetConsoleWindowInfo(). First version of my function:
bool SetWindowSize(size_t width, size_t height)
{
HANDLE output_handle = ::GetStdHandle(STD_OUTPUT_HANDLE);
if(output_handle == INVALID_HANDLE_VALUE)
return false;
COORD coord = {};
coord.X = static_cast<SHORT>(width);
coord.Y = static_cast<SHORT>(height);
if(::SetConsoleScreenBufferSize(output_handle, coord) == FALSE)
return false;
SMALL_RECT rect = {};
rect.Bottom = coord.X - 1;
rect.Right = coord.Y - 1;
return (::SetConsoleWindowInfo(output_handle, TRUE, &rect) != FALSE);
}
SetConsoleScreenBufferSize() will work not for all values. From documentation:
The specified width and height cannot be less than the width and
height of the console screen buffer's window
Lets try to get current window's size and call our function. To get window size, I need GetConsoleScreenBufferInfo() function. main() test code:
HANDLE output_handle = ::GetStdHandle(STD_OUTPUT_HANDLE);
if(output_handle == INVALID_HANDLE_VALUE)
return 0;
CONSOLE_SCREEN_BUFFER_INFO info = {};
if(::GetConsoleScreenBufferInfo(output_handle, &info) == FALSE)
return 0;
size_t width = info.srWindow.Right - info.srWindow.Left;
size_t height = info.srWindow.Bottom - info.srWindow.Top;
bool suc = SetWindowSize(width + 1, height + 1);
In this case SetConsoleScreenBufferSize() works fine. Next function is SetConsoleWindowInfo(). This function will work in case:
The function fails if the specified window rectangle extends beyond
the boundaries of the console screen buffer. This means that the Top
and Left members of the lpConsoleWindow rectangle (or the calculated
top and left coordinates, if bAbsolute is FALSE) cannot be less than
zero. Similarly, the Bottom and Right members (or the calculated
bottom and right coordinates) cannot be greater than (screen buffer
height – 1) and (screen buffer width – 1), respectively. The function
also fails if the Right member (or calculated right coordinate) is
less than or equal to the Left member (or calculated left coordinate)
or if the Bottom member (or calculated bottom coordinate) is less than
or equal to the Top member (or calculated top coordinate).
In our case, the values of rectangle are the same (because Left and Top are zeroes) as values of info.srWindow rectangle after call of GetConsoleScreenBufferInfo(). But! SetConsoleWindowInfo() fails with next ::GetLastError()
#err,hr ERROR_INVALID_PARAMETER : The parameter is incorrect. unsigned int
If I swap calls of this two functions:
bool SetWindowSize(size_t width, size_t height)
{
HANDLE output_handle = ::GetStdHandle(STD_OUTPUT_HANDLE);
if(output_handle == INVALID_HANDLE_VALUE)
return false;
SMALL_RECT rect = {};
rect.Bottom = static_cast<SHORT>(width);
rect.Right = static_cast<SHORT>(height);
if(::SetConsoleWindowInfo(output_handle, TRUE, &rect) == FALSE)
return false;
COORD coord = {};
coord.X = rect.Bottom + 1;
coord.Y = rect.Right + 1;
return (::SetConsoleScreenBufferSize(output_handle, coord) != FALSE);
}
then I will have the same error.
So, how can I use SetConsoleScreenBufferSize() and SetConsoleWindowInfo() correctly ?
SetConsoleWindowInfo() does not reposition the console window on the screen. The name of this function is misleading. It rather scrolls the current visible portion inside the console window. See this sample here.
If you want to set the position of a console window that runs your programm, use code such as:
HWND hwnd = GetConsoleWindow();
RECT rect = {100, 100, 300, 500};
MoveWindow(hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,TRUE);
From the TurboVision port:
void TDisplay::setCrtMode( ushort mode )
{
int oldr = getRows();
int oldc = getCols();
int cols = uchar(mode >> 8);
int rows = uchar(mode);
if ( cols == 0 ) cols = oldc;
if ( rows == 0 ) rows = oldr;
checksize(rows, cols);
COORD newSize = { cols, rows };
SMALL_RECT rect = { 0, 0, cols-1, rows-1 };
if ( oldr <= rows )
{
if ( oldc <= cols )
{ // increasing both dimensions
BUFWIN:
SetConsoleScreenBufferSize( TThreads::chandle[cnOutput], newSize );
SetConsoleWindowInfo( TThreads::chandle[cnOutput], True, &rect );
}
else
{ // cols--, rows+
SMALL_RECT tmp = { 0, 0, cols-1, oldr-1 };
SetConsoleWindowInfo( TThreads::chandle[cnOutput], True, &tmp );
goto BUFWIN;
}
}
else
{
if ( oldc <= cols )
{ // cols+, rows--
SMALL_RECT tmp = { 0, 0, oldc-1, rows-1 };
SetConsoleWindowInfo( TThreads::chandle[cnOutput], True, &tmp );
goto BUFWIN;
}
else
{ // cols--, rows--
SetConsoleWindowInfo( TThreads::chandle[cnOutput], True, &rect );
SetConsoleScreenBufferSize( TThreads::chandle[cnOutput], newSize );
}
}
GetConsoleScreenBufferInfo( TThreads::chandle[cnOutput], &TThreads::sbInfo );
}
ushort TDisplay::getRows()
{
GetConsoleScreenBufferInfo( TThreads::chandle[cnOutput], &TThreads::sbInfo );
return TThreads::sbInfo.dwSize.Y;
}
ushort TDisplay::getCols()
{
GetConsoleScreenBufferInfo( TThreads::chandle[cnOutput], &TThreads::sbInfo );
return TThreads::sbInfo.dwSize.X;
}
I solved this issue by making these functions which can get/set the console window/buffer sizes in characters taking into account increasing the buffer size if needed, console font size, window borders and all that jazz.
The variables at play here to understand:
Windows have a client area, which is the coordinates (in pixels) excluding the borders
Windows have a window area, which is the coordinates (in pixels) including the borders
Console has a view area, which is the window size in characters
Console has a screen buffer, which is the scrollable buffer size in characters
Console has a font size, which is the character size in coordinates (pixels)
Console's screen buffer cannot be smaller than the view area
You need to correctly mix and match these around to achieve the desired result.
These functions are plug-n-play so you don't need to worry about none of that though.
All functions return TRUE (1) on success and FALSE (0) on error.
Set Console Window Size
static BOOL SetConsoleSize(int cols, int rows) {
HWND hWnd;
HANDLE hConOut;
CONSOLE_FONT_INFO fi;
CONSOLE_SCREEN_BUFFER_INFO bi;
int w, h, bw, bh;
RECT rect = {0, 0, 0, 0};
COORD coord = {0, 0};
hWnd = GetConsoleWindow();
if (hWnd) {
hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConOut && hConOut != (HANDLE)-1) {
if (GetCurrentConsoleFont(hConOut, FALSE, &fi)) {
if (GetClientRect(hWnd, &rect)) {
w = rect.right-rect.left;
h = rect.bottom-rect.top;
if (GetWindowRect(hWnd, &rect)) {
bw = rect.right-rect.left-w;
bh = rect.bottom-rect.top-h;
if (GetConsoleScreenBufferInfo(hConOut, &bi)) {
coord.X = bi.dwSize.X;
coord.Y = bi.dwSize.Y;
if (coord.X < cols || coord.Y < rows) {
if (coord.X < cols) {
coord.X = cols;
}
if (coord.Y < rows) {
coord.Y = rows;
}
if (!SetConsoleScreenBufferSize(hConOut, coord)) {
return FALSE;
}
}
return SetWindowPos(hWnd, NULL, rect.left, rect.top, cols*fi.dwFontSize.X+bw, rows*fi.dwFontSize.Y+bh, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
}
}
}
}
}
}
return FALSE;
}
/* usage */
SetConsoleSize(80, 40);
Get Console Window Size
static BOOL GetConsoleSize(int* cols, int* rows) {
HWND hWnd;
HANDLE hConOut;
CONSOLE_FONT_INFO fi;
int w, h;
RECT rect = {0, 0, 0, 0};
hWnd = GetConsoleWindow();
if (hWnd) {
hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConOut && hConOut != (HANDLE)-1) {
if (GetCurrentConsoleFont(hConOut, FALSE, &fi)) {
if (GetClientRect(hWnd, &rect)) {
w = rect.right-rect.left;
h = rect.bottom-rect.top;
*cols = w / fi.dwFontSize.X;
*rows = h / fi.dwFontSize.Y;
return TRUE;
}
}
}
}
return FALSE;
}
/* usage */
int cols, rows;
GetConsoleSize(&cols, &rows);
Set Console Buffer Size
static BOOL SetConsoleBufferSize(int cols, int rows) {
HANDLE hConOut;
CONSOLE_SCREEN_BUFFER_INFO bi;
COORD coord = {0, 0};
hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConOut && hConOut != (HANDLE)-1) {
if (GetConsoleScreenBufferInfo(hConOut, &bi)) {
coord.X = cols;
coord.Y = rows;
return SetConsoleScreenBufferSize(hConOut, coord);
}
}
return FALSE;
}
/* usage */
SetConsoleBufferSize(80, 300);
Get Console Buffer Size
static BOOL GetConsoleBufferSize(int* cols, int* rows) {
HANDLE hConOut;
CONSOLE_SCREEN_BUFFER_INFO bi;
hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConOut && hConOut != (HANDLE)-1) {
if (GetConsoleScreenBufferInfo(hConOut, &bi)) {
*cols = bi.dwSize.X;
*rows = bi.dwSize.Y;
return TRUE;
}
}
return FALSE;
}
/* usage */
int cols, rows;
GetConsoleBufferSize(&cols, &rows);
For Example: if my image width and height is 1600x2000 and client window width and height is 900x600.. now I want to fit the image horizontally. For that I will bring down the image width(1600) to window width(900) and also to maintain the original aspect ratio of the image i will also bring down the height of the image(2000) to 1125. for that I have used the below logic in FITHORIZONTAL Then I will use below wm_size code to set the scroll bar position and thumb length. Unfortunately the full image is not shown even though the thumb tracker has reached the end of the scroll bar.
switch(FitType)
{
case FITHORIZONTAL:
//find out whether resize percentage is decrease or increase
if(ActualImageWidth > WindowWidth) //resize decreasing
{
//find out the pecentage of decreased value
resizedPercent = WindowWidth/ActualImageWidth;
} else //resize increasing
{
//find out the pecentage of increased value
resizedPercent = ActualImageWidth/WindowWidth;
}
ResizedWidth = iWndwidth;
ResizedHeight = ActualImageHeight * resizedPercent;
break;
}
case WM_SIZE :
{
GetWindowRect(hWnd, &rect);
WndWidth = rect.right - rect.left;
WndHeight = rect.bottom - rect.top;
GetScrollInfo(hWnd, SB_VERT, &si);
int yMaxScroll = max((int)ResizedHeight - WndHeight, 0);
int yCurrentScroll = min(si.nPos, yMaxScroll);
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMin = 0;
si.nMax = ResizedHeight;
si.nPage = WndHeight;
si.nPos = 0;
SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
InvalidateRect(hWnd, &rect, true);
return 0;
}