NO

Author Topic: Center Window  (Read 2095 times)

Grincheux

  • Guest
Center Window
« on: October 14, 2020, 05:42:41 PM »
Many people wrote function for centering a window but the function always use the first monitor !
They used GetSystemMetrics but the function use the primary monitor.
I did not made better, because I used SystemParametersInfo which returns results for the first monitor !
The result always was not very pretty.
Today I use an other method.
I consider that I have not two monitors but a virtual monitor.
First Monitor 1366 x 1280 and the 1600 x 1280
So, when I create a window I have a virtual monitor of 2966 x 1280.
If I want to create the window on the first monitor the X coordinate is 0 and if I want to have the window
on the second monitor the X coordinate is bigger than 1366.
Don't forget to store the monitor handle.

Code: [Select]
#define WIN32_LEAN_AND_MEAN  /* speed up compilations */
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <wchar.h>
#include "main.h"

#define NELEMS(a) (sizeof(a) / sizeof((a)[0]))

/** Global variables ********************************************************/

static HINSTANCE hInstance ;

void CenterWindow(HWND __hWnd)
{
   HMONITOR   _hMonitor ;
   MONITORINFO   _MonitorInfo ;
   RECT      _WindowRect ;
   int         _WindowWidth, _WindowHeight ;

   GetWindowRect(__hWnd,&_WindowRect) ;

   _WindowWidth = _WindowRect.right  - _WindowRect.left ;
   _WindowHeight = _WindowRect.bottom - _WindowRect.top ;

   _hMonitor = MonitorFromRect(&_WindowRect,MONITOR_DEFAULTTOPRIMARY) ;
   _MonitorInfo.cbSize = sizeof(MONITORINFO) ;

   GetMonitorInfo(_hMonitor,&_MonitorInfo) ;

   _WindowRect.left   = _MonitorInfo.rcWork.left + (_MonitorInfo.rcWork.right  - _MonitorInfo.rcWork.left - _WindowWidth) / 2 ;
   _WindowRect.top      = _MonitorInfo.rcWork.top  + (_MonitorInfo.rcWork.bottom - _MonitorInfo.rcWork.top  - _WindowHeight) / 2 ;
   _WindowRect.right   = _WindowRect.left + _WindowWidth ;
   _WindowRect.bottom   = _WindowRect.top  + _WindowHeight ;

   SetWindowPos(__hWnd,NULL,_WindowRect.left,_WindowRect.top,0,0,SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE) ;

   return ;
}

static LRESULT CALLBACK AboutDlgProc(HWND __hDlg,UINT __uMsg,WPARAM __wParam,LPARAM __lParam)
{
   switch(__uMsg)
   {
      case   WM_INITDIALOG :
                        return (TRUE) ;

      case   WM_COMMAND :
                        switch(__wParam)
                        {
                           case IDOK :
                           case IDCANCEL :
                                       EndDialog(__hDlg,TRUE) ;
                                       return (TRUE) ;
                        }

                        break ;
   }

   return (FALSE) ;
}

static void Main_OnPaint(HWND __hWnd)
{
   PAINTSTRUCT ps ;
   RECT rc ;

   BeginPaint(__hWnd,&ps) ;
   GetClientRect(__hWnd,&rc) ;
   DrawText(ps.hdc,L"Hello,Windows!",-1,&rc,DT_SINGLELINE|DT_CENTER|DT_VCENTER) ;
   EndPaint(__hWnd,&ps) ;

   return ;
}

static void Main_OnCommand(HWND __hWnd,int id,HWND hwndCtl,UINT codeNotify)
{
   switch(id)
   {
      case IDM_ABOUT:
         DialogBox(hInstance,MAKEINTRESOURCE(DLG_ABOUT),__hWnd,(DLGPROC)AboutDlgProc) ;

      /* TODO: Enter more commands here */
   }

   return ;
}

static void Main_OnDestroy(HWND __hWnd)
{
   PostQuitMessage(0) ;

   return ;
}

static LRESULT CALLBACK MainWndProc(HWND __hWnd,UINT _Msg,WPARAM wParam,LPARAM lParam)
{
   switch(_Msg)
   {
      HANDLE_MSG(__hWnd,WM_PAINT,Main_OnPaint) ;
      HANDLE_MSG(__hWnd,WM_COMMAND,Main_OnCommand) ;
      HANDLE_MSG(__hWnd,WM_DESTROY,Main_OnDestroy) ;
      /* TODO: enter more messages here */
      default:
         return DefWindowProc(__hWnd,_Msg,wParam,lParam) ;
   }

   return (0) ;
}

int APIENTRY wWinMain(HINSTANCE __hInstance,HINSTANCE __hPrevInstance,WCHAR *__pszCmdLine,int __nCmdShow)
{
   INITCOMMONCONTROLSEX   _Icc ;
   WNDCLASS            _Wc ;
   HWND               _hWnd ;
   MSG                  _Msg ;

   _Wc.hInstance = hInstance = __hInstance ;

   _Icc.dwSize = sizeof(_Icc) ;
   _Icc.dwICC = ICC_WIN95_CLASSES /*|ICC_COOL_CLASSES|ICC_DATE_CLASSES|ICC_PAGESCROLLER_CLASS|ICC_USEREX_CLASSES|... */ ;

   InitCommonControlsEx(&_Icc) ;

   /* Register the main window class */

   _Wc.lpszClassName = L"MonitorTClass" ;
   _Wc.lpfnWndProc = MainWndProc ;
   _Wc.style = CS_OWNDC|CS_VREDRAW|CS_HREDRAW|CS_DROPSHADOW ;
   _Wc.hIcon = LoadIcon(hInstance,MAKEINTRESOURCE(IDR_ICO_MAIN)) ;
   _Wc.hCursor = LoadCursor(NULL,IDC_ARROW) ;
   _Wc.hbrBackground =(HBRUSH)(COLOR_WINDOW + 1) ;
   _Wc.lpszMenuName = MAKEINTRESOURCE(IDR_MNU_MAIN) ;
   _Wc.cbClsExtra = 0 ;
   _Wc.cbWndExtra = 0 ;

   if(!RegisterClass(&_Wc))
      return (1) ;

   /* Create the main window */
   _hWnd = CreateWindow(L"MonitorTClass",L"MonitorTest Program",WS_OVERLAPPEDWINDOW|WS_HSCROLL|WS_VSCROLL,
      1500,0,800,600,NULL,NULL,hInstance,NULL) ;
   if(!_hWnd) return (1) ;

   /* Show and paint the main window */
   ShowWindow(_hWnd,__nCmdShow) ;
   UpdateWindow(_hWnd) ;

   CenterWindow(_hWnd) ;

   /* Pump messages until we are done */
   while(GetMessage(&_Msg,NULL,0,0) > 0)
   {
      TranslateMessage(&_Msg) ;
      DispatchMessage(&_Msg) ;
   }

   return (_Msg.wParam) ;
}
I have not invented that but tooke it from https://docs.microsoft.com/en-us/windows/win32/gdi/positioning-objects-on-a-multiple-display-setup.

If you only have one monitor you can change

   _hMonitor = MonitorFromRect(&_WindowRect,MONITOR_DEFAULTTONEAREST) ;
by
   _hMonitor = MonitorFromRect(&_WindowRect,MONITOR_DEFAULTTOPRIMARY) ;

I wrote a demonstration program display a window on the second monitor.
The goal is to create a window of 800 x 600.
The X coordinate is 1500.
If there is only one monitor the window is centered on it.

I hope you understood what I wante to do and that it will be useful.
« Last Edit: October 14, 2020, 05:48:50 PM by Grincheux »

Offline John Z

  • Member
  • *
  • Posts: 860
Re: Center Window
« Reply #1 on: October 15, 2020, 02:47:11 PM »
Thanks Grincheux,

Very interesting.  I've not considered dual screens before so I'm surprised there is not a windows API to handle this.  At least I would expect to be able to get the number of monitors.  The code looks useful.  I wonder if there is an API to get the actual resolution of each monitor so that the 'virtual' monitor would accurately reflect the true overall size? The what if H and V are both different?  should the 'virtual' monitor be the sum of maximums or minimums ;)

Good post to get thinking about dual, maybe even triple, monitor handling.

John Z

Grincheux

  • Guest
Re: Center Window
« Reply #2 on: October 15, 2020, 03:01:07 PM »
MONITORINFO structure (winuser.h)rcMonitor
A RECT structure that specifies the display monitor rectangle, expressed in virtual-screen coordinates.
Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.

rcWork A RECT structure that specifies the work area rectangle of the display monitor,
expressed in virtual-screen coordinates. Note that if the monitor is not the primary display monitor,
some of the rectangle's coordinates may be negative values.

That would answer to your question and GetSystemMetrics(SM_CMONITORS) gives you the number of monitors.
« Last Edit: October 15, 2020, 03:04:58 PM by Grincheux »

Grincheux

  • Guest
Re: Center Window
« Reply #3 on: October 15, 2020, 03:09:12 PM »
I have modified the CenterWindow function. Now it returns the handle of the monitor on which the window is displayed.
Code: [Select]
HMONITOR CenterWindow(HWND __hWnd)
{
   HMONITOR   _hMonitor ;
   MONITORINFO   _MonitorInfo ;
   RECT      _WindowRect ;
   int         _WindowWidth, _WindowHeight ;

   GetWindowRect(__hWnd,&_WindowRect) ;

   _WindowWidth = _WindowRect.right  - _WindowRect.left ;
   _WindowHeight = _WindowRect.bottom - _WindowRect.top ;

   if(GetSystemMetrics(SM_CMONITORS) == 1)   _hMonitor = MonitorFromRect(&_WindowRect,MONITOR_DEFAULTTOPRIMARY) ;
   else                           _hMonitor = MonitorFromRect(&_WindowRect,MONITOR_DEFAULTTONEAREST) ;

   _MonitorInfo.cbSize = sizeof(MONITORINFO) ;

   GetMonitorInfo(_hMonitor,&_MonitorInfo) ;

   _WindowRect.left   = _MonitorInfo.rcWork.left + (_MonitorInfo.rcWork.right  - _MonitorInfo.rcWork.left - _WindowWidth) / 2 ;
   _WindowRect.top      = _MonitorInfo.rcWork.top  + (_MonitorInfo.rcWork.bottom - _MonitorInfo.rcWork.top  - _WindowHeight) / 2 ;
   _WindowRect.right   = _WindowRect.left + _WindowWidth ;
   _WindowRect.bottom   = _WindowRect.top  + _WindowHeight ;

   SetWindowPos(__hWnd,NULL,_WindowRect.left,_WindowRect.top,0,0,SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE) ;

   return (_hMonitor) ;
}

Offline John Z

  • Member
  • *
  • Posts: 860
Re: Center Window
« Reply #4 on: October 16, 2020, 01:18:25 PM »
Nice improvement!

John Z