Center Window Over Multiple Monitors...

Share your advanced PureBasic knowledge/code with the community.
User avatar
Teddy Rogers
User
User
Posts: 98
Joined: Sun Feb 23, 2014 2:05 am
Location: Australia
Contact:

Center Window Over Multiple Monitors...

Post by Teddy Rogers »

Here is some small code to center your window in the middle of the screen. I initially looked for something similar in PureBasic and had to result to Windows API and did not find similar code in the forums. This method should work across multiple monitors quickly and easily, even when the screen resolution or orientation differs from one-to-the-next. Feel free to modify and use the code...

Code: Select all

Declare CenterWindow(hWnd, HMONITOR, WWidth, WHeight)

Define pt.Point

WWidth = 400
WHeight = 200

If OpenWindow(0, 0, 0, WWidth, WHeight, "Center window over multiple monitors...", #PB_Window_SystemMenu | #PB_Window_SizeGadget)
  hWnd = WindowID(0)
  
  Repeat
    MyEvent = WindowEvent()
    
    If GetCursorPos_(@pt)
      HMONITOR = MonitorFromPoint_(PeekQ(@pt), #MONITOR_DEFAULTTONEAREST)
      
      If HMONITOR <> HMONITOROLD
        If CenterWindow(hWnd, HMONITOR, WWidth, WHeight)
          HMONITOROLD = HMONITOR
          Debug "Window changed..."
        EndIf
      EndIf
    EndIf
    
  Until MyEvent = #PB_Event_CloseWindow
EndIf

Procedure CenterWindow(hWnd, HMONITOR, WWidth, WHeight)
  Protected Left, Top
  
  Define mi.MONITORINFO
  
  mi\cbSize = SizeOf(mi)
  
  If GetMonitorInfo_(HMONITOR, @mi)
    
    Left = mi\rcMonitor\left + (mi\rcMonitor\right - mi\rcMonitor\left - WWidth) / 2
    Top = mi\rcMonitor\top + (mi\rcMonitor\bottom - mi\rcMonitor\top - WHeight) / 2
    
    SetWindowPos_(hWnd, #Null, Left, Top, 0, 0, #SWP_NOSIZE | #SWP_NOZORDER | #SWP_NOACTIVATE) 
  Else
    ProcedureReturn #False
  EndIf
  
  ProcedureReturn #True
EndProcedure
Ted.
Last edited by Teddy Rogers on Fri Nov 21, 2014 10:51 am, edited 1 time in total.
infratec
Always Here
Always Here
Posts: 7622
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Center Window Over Multiple Monitors...

Post by infratec »

Hi,

since I like 'crossplatform',
here is a 'Pure' Basic solution:

Code: Select all

EnableExplicit

Structure DesktopInfoStructure
  X1.i
  Y1.i
  X2.i
  Y2.i
EndStructure


Procedure.i GetDesktop(List DesktopList.DesktopInfoStructure())
 
  Protected.i Result, X, Y
 
  Result = -1
 
  ExamineDesktops()
 
  X = DesktopMouseX()
  Y = DesktopMouseY()
 
  ForEach DesktopList()
    With DesktopList()
      If X >= \X1 And X <= \X2 And Y >= \Y1 And Y <= \Y2
        Result = ListIndex(DesktopList())
        Break
      EndIf
    EndWith
  Next
 
  ProcedureReturn Result
 
EndProcedure




Define.i Event, Desktops, CurrentDesktop, OldDesktop, Exit, i

NewList DesktopList.DesktopInfoStructure()

Desktops = ExamineDesktops() - 1
If Desktops
  For i = 0 To Desktops
    AddElement(DesktopList())
    DesktopList()\X1 = DesktopX(i)
    DesktopList()\Y1 = DesktopY(i)
    DesktopList()\X2 = DesktopList()\X1 + DesktopWidth(i)
    DesktopList()\Y2 = DesktopList()\Y1 + DesktopHeight(i)
  Next i
EndIf

OldDesktop = -1


If OpenWindow(0, 0, 0, 400, 200, "Center window over multiple monitors...", #PB_Window_SystemMenu | #PB_Window_SizeGadget)
  
  PostEvent(#PB_Event_MoveWindow)
  
  Repeat
    Event = WaitWindowEvent()
   
    Select Event
      Case #PB_Event_CloseWindow
        Exit = #True
       
      Case #PB_Event_MoveWindow
        CurrentDesktop = GetDesktop(DesktopList())
        If CurrentDesktop <> OldDesktop
          With DesktopList()
            ResizeWindow(0, \X1 + (\X2 - \X1 - WindowWidth(0)) / 2, \Y1 + (\Y2 - \Y1 - WindowHeight(0)) / 2, #PB_Ignore, #PB_Ignore)
          EndWith
          OldDesktop = CurrentDesktop
        EndIf
       
    EndSelect
   
  Until Exit
EndIf
P.S.: Your solution was not working on my PC: Win 7 x64 with 2 monitors
It get always the same monitor handle back from MonitorFromPoint_()

Bernd
User avatar
Teddy Rogers
User
User
Posts: 98
Joined: Sun Feb 23, 2014 2:05 am
Location: Australia
Contact:

Re: Center Window Over Multiple Monitors...

Post by Teddy Rogers »

Hmm... its working fine here on both Windows 7 x64 and Windows 8.1 x64 compiled with PB 5.30 and PB 5.22 LTS. What version of PB are you running? Possibly you may need to define pt.Point with...

Code: Select all

Define pt.Point
Out of curiosity has anyone had any joy getting MONITORINFOEX working in PB? I can get the structures size but can't retrieve anything from it. It's nearly identical to MONITORINFO but with an extra member for szDevice...

http://msdn.microsoft.com/en-us/library ... 85%29.aspx

Ted.
infratec
Always Here
Always Here
Posts: 7622
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Center Window Over Multiple Monitors...

Post by infratec »

Hi,

I use PB 5.31 x86

And I have also extended your version with EnableExplicit().
So everything was right.

Bernd
User avatar
Teddy Rogers
User
User
Posts: 98
Joined: Sun Feb 23, 2014 2:05 am
Location: Australia
Contact:

Re: Center Window Over Multiple Monitors...

Post by Teddy Rogers »

Thanks for the info, I just tried compiling with PB x32 compiler and there is an overflow bug, it can be fixed by adding, "Define pt.Point". I corrected the original post...

Ted.
infratec
Always Here
Always Here
Posts: 7622
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Center Window Over Multiple Monitors...

Post by infratec »

Hi,

optimized version, which only fits the needs:

Code: Select all

EnableExplicit


Procedure.i CenterWindowOnDesktop(Window)
 
  Protected.i Result, Desktops, X, Y, X1, Y1, X2, Y2, i
  Static OldDesktop.i
  
  
  Desktops = ExamineDesktops() - 1
  
  X = DesktopMouseX()
  Y = DesktopMouseY()
  
  For i = 0 To Desktops
    X1 = DesktopX(i)
    Y1 = DesktopY(i)
    X2 = X1 + DesktopWidth(i)
    Y2 = Y1 + DesktopHeight(i)
    If X >= X1 And X <= X2 And Y >= Y1 And Y <= Y2
      If i <> OldDesktop
        ResizeWindow(Window, X1 + (X2 - X1 - WindowWidth(Window)) / 2, Y1 + (Y2 - Y1 - WindowHeight(Window)) / 2, #PB_Ignore, #PB_Ignore)
        OldDesktop = i
        Result = #True
      EndIf
      Break
    EndIf
  Next
  
  ProcedureReturn Result
 
EndProcedure



Define.i Event, Exit


If OpenWindow(0, 0, 0, 400, 200, "Center window over multiple monitors...", #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_ScreenCentered)
  
  Repeat
    Event = WaitWindowEvent()
   
    Select Event
      Case #PB_Event_CloseWindow
        Exit = #True
       
      Case #PB_Event_MoveWindow
        If CenterWindowOnDesktop(0)
          Debug "Centered!"
        EndIf
          
       
    EndSelect
   
  Until Exit
EndIf
Bernd
User avatar
Teddy Rogers
User
User
Posts: 98
Joined: Sun Feb 23, 2014 2:05 am
Location: Australia
Contact:

Re: Center Window Over Multiple Monitors...

Post by Teddy Rogers »

Nice! So I don't feel left out here is a version similar to yours using Windows API's; when the window moves to a new screen it will automatically snap the window to the center but can be repositioned whilst still on the existing screen...

Code: Select all

Declare CenterWindowOnDesktop(hWnd)

If OpenWindow(0, 0, 0, 400, 200, "Center window over multiple monitors...", #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_ScreenCentered)
  
  Repeat
    MyEvent = WaitWindowEvent()
    
    Select MyEvent
        
      Case #PB_Event_MoveWindow
        If CenterWindowOnDesktop(WindowID(0))
          Debug "Window centered..."
        EndIf
        
    EndSelect
    
  Until MyEvent = #PB_Event_CloseWindow
EndIf

Procedure CenterWindowOnDesktop(hWnd)
  Protected Left, Top 
  Static HMONITOROLD
  
  Define mi.MONITORINFO
  Define lpRect.RECT
  
  GetWindowRect_(hWnd, @lpRect)
  HMONITOR = MonitorFromRect_(lpRect, #MONITOR_DEFAULTTONEAREST)
  
  If HMONITOR <> HMONITOROLD
    mi\cbSize = SizeOf(mi)
    
    If GetMonitorInfo_(HMONITOR, @mi)
      
      Left = mi\rcMonitor\left + (mi\rcMonitor\right - mi\rcMonitor\left - (lpRect\right - lpRect\left)) / 2
      Top = mi\rcMonitor\top + (mi\rcMonitor\bottom - mi\rcMonitor\top - (lpRect\bottom - lpRect\top)) / 2
      
      SetWindowPos_(hWnd, #Null, Left, Top, 0, 0, #SWP_NOSIZE | #SWP_NOZORDER | #SWP_NOACTIVATE) 
      
      HMONITOROLD = HMONITOR
      
      ProcedureReturn #True
    EndIf
  EndIf
  
  ProcedureReturn #False
EndProcedure
Ted.
User avatar
chi
Addict
Addict
Posts: 1087
Joined: Sat May 05, 2007 5:31 pm
Location: Austria

Re: Center Window Over Multiple Monitors...

Post by chi »

Teddy Rogers wrote:Out of curiosity has anyone had any joy getting MONITORINFOEX working in PB? I can get the structures size but can't retrieve anything from it. It's nearly identical to MONITORINFO but with an extra member for szDevice...
PB's MONITORINFOEX isn't unicode compatible so you need to define your own structure.

Code: Select all

Structure MONITORINFOEX2
  cbSize.i
  rcMonitor.RECT
  rcWork.RECT
  dwFlags.i
  szDevice.s{#CCHDEVICENAME}
EndStructure

MonInfo.MONITORINFOEX\cbSize = SizeOf(MONITORINFOEX)
GetMonitorInfo_(MonitorFromPoint_(pt.POINT, #MONITOR_DEFAULTTONEAREST), @MonInfo)

Debug MonInfo\szDevice
Debug MonInfo\rcMonitor\left
Debug MonInfo\rcMonitor\top
Debug MonInfo\rcMonitor\right
Debug MonInfo\rcMonitor\bottom
Et cetera is my worst enemy
User avatar
Teddy Rogers
User
User
Posts: 98
Joined: Sun Feb 23, 2014 2:05 am
Location: Australia
Contact:

Re: Center Window Over Multiple Monitors...

Post by Teddy Rogers »

Yes, I realised earlier on after I tried disabling unicode. This is how I had it working by modifying the code in the first post...

Code: Select all

Declare CenterWindow(hWnd, HMONITOR, WWidth, WHeight)

Define pt.Point

WWidth = 400
WHeight = 200

If OpenWindow(0, 0, 0, WWidth, WHeight, "Center window over multiple monitors...", #PB_Window_SystemMenu | #PB_Window_SizeGadget)
  hWnd = WindowID(0)
  
  Repeat
    MyEvent = WindowEvent()
    
    If GetCursorPos_(@pt)
      HMONITOR = MonitorFromPoint_(PeekQ(@pt), #MONITOR_DEFAULTTONEAREST)
      
      If HMONITOR <> HMONITOROLD
        If CenterWindow(hWnd, HMONITOR, WWidth, WHeight)
          HMONITOROLD = HMONITOR
          Debug "Window changed..."
        EndIf
      EndIf
    EndIf
    
  Until MyEvent = #PB_Event_CloseWindow
EndIf

Procedure CenterWindow(hWnd, HMONITOR, WWidth, WHeight)
  Protected Left, Top
  
  Structure MONITORINFOEXA
    cbSize.l
    rcMonitor.RECT
    rcWork.RECT
    dwFlags.l
    szDevice.s {#CCHDEVICENAME}
  EndStructure
  
  Define mi.MONITORINFOEXA
  
  mi\cbSize = SizeOf(mi)
  
  If GetMonitorInfo_(HMONITOR, @mi)
    
    Left = mi\rcMonitor\left + (mi\rcMonitor\right - mi\rcMonitor\left - WWidth) / 2
    Top = mi\rcMonitor\top + (mi\rcMonitor\bottom - mi\rcMonitor\top - WHeight) / 2
    
    SetWindowPos_(hWnd, #Null, Left, Top, 0, 0, #SWP_NOSIZE | #SWP_NOZORDER | #SWP_NOACTIVATE)
  Else
    ProcedureReturn #False
  EndIf
  
  ProcedureReturn #True
EndProcedure
Ted.
Post Reply