Page 1 of 1

Center Window Over Multiple Monitors...

Posted: Fri Nov 21, 2014 5:47 am
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.

Re: Center Window Over Multiple Monitors...

Posted: Fri Nov 21, 2014 9:58 am
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

Re: Center Window Over Multiple Monitors...

Posted: Fri Nov 21, 2014 10:21 am
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.

Re: Center Window Over Multiple Monitors...

Posted: Fri Nov 21, 2014 10:24 am
by infratec
Hi,

I use PB 5.31 x86

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

Bernd

Re: Center Window Over Multiple Monitors...

Posted: Fri Nov 21, 2014 10:55 am
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.

Re: Center Window Over Multiple Monitors...

Posted: Fri Nov 21, 2014 12:21 pm
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

Re: Center Window Over Multiple Monitors...

Posted: Fri Nov 21, 2014 3:02 pm
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.

Re: Center Window Over Multiple Monitors...

Posted: Fri Nov 21, 2014 3:09 pm
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

Re: Center Window Over Multiple Monitors...

Posted: Fri Nov 21, 2014 5:07 pm
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.