Page 1 of 1

WindowForceVisible or how to get back lost windows :)

Posted: Sat Nov 17, 2007 8:29 am
by Rescator

Code: Select all

EnableExplicit

;Checks if the window is in a visible area on screen with s specified margin if any.
;When is this truly usefull?
;1. When by mistake the x,y position was saved while minimized.
;2. When x,y position saved is wrong.
;3. When the user remove/change/swap multidisplay desktop setup or re-align them.
;4. When the user manages to move to window so far to the side he can't easily grab it
;   and in panic or anger quits/kill the program and try to restart it but the settings
;   was saved with the weird x,y position.
;5. Probably more reasons...Why this behaviour isn't part of the OS is mind-boggling,
;   Would a Open_Viewable window flag been that hard to add to the OS? *laughs*

;If not visible it will attempt to make it visible and return true if it worked.
;False is returned if it fail for some reason, to allow you to handle the error yourself.
;Works fully with Windows 98 or later (multimonitor), there is a fallback for W95 and NT.

;Note! The procedure will un-minimize, move then re-minimize a window if it was previously
;minimized, this lets you open the window in a minimized state without conflict.
;To avoid the window flashing/jumping it might be wise to open the window hidden.

;This procedure is best used at program startup, right after you have loaded the program
;settings for example (x,y,minimized or maximized et.al.) and opened the window hidden.

;Tip! To open the window hidden use #PB_Window_Invisible as a window flag
;and HideWindow(#Window,#False) when done making the window, adding gadgets and so on
;Tip! To Restore (un-minimize) a window, use SetWindowState(#Window,#PB_Window_Normal)

Structure MONITORINFO
 cbSize.l
 rcMonitor.RECT
 rcWork.RECT
 dwFlags.l
EndStructure

#MONITOR_DEFAULTTONEAREST=$00000002

Procedure.l WindowForceVisible(window.l,margin.l=0)
Protected result.l=#False,hwnd.l,rect.RECT,hmonitor.l,mi.MONITORINFO,noerror.l=#False,state.l
 If IsWindow(window)
  hwnd=WindowID(window)
  state=GetWindowState(window)
  If state=#PB_Window_Minimize
   SetWindowState(window,#PB_Window_Normal)
  EndIf
  If GetWindowRect_(hwnd,rect)
   mi\cbSize=SizeOf(MONITORINFO)
   If OSVersion()>=#PB_OS_Windows_98
    hmonitor=MonitorFromWindow_(hwnd,#MONITOR_DEFAULTTONEAREST)
    If hmonitor
     noerror=GetMonitorInfo_(hmonitor,mi)
    Else
     noerror=#False
    EndIf
   Else ;W95,NT3,NT4 fallback.
    noerror=SystemParametersInfo_(#SPI_GETWORKAREA,#Null,mi\rcWork,#Null)
   EndIf
   If noerror
    If (rect\left<(mi\rcWork\left-margin)) Or (rect\top<(mi\rcWork\top-margin)) Or (rect\right>(mi\rcWork\right+margin)) Or (rect\bottom>(mi\rcWork\bottom+margin))
     rect\left=mi\rcWork\left+((mi\rcWork\right-mi\rcWork\left)-(rect\right-rect\left))/2
     rect\top=mi\rcWork\top+((mi\rcWork\bottom-mi\rcWork\top)-(rect\bottom-rect\top))/2
     If SetWindowPos_(hwnd,#Null,rect\left,rect\top,#Null,#Null,#SWP_NOACTIVATE|#SWP_NOZORDER|#SWP_NOSIZE)
      result=#True ;Window moved and should now be visible again.
     EndIf
    Else ;No need to move the window as it is within margin, window visible.
     result=#True
    EndIf
   EndIf
  EndIf
  If state=#PB_Window_Minimize
   SetWindowState(window,state)
  EndIf
 EndIf
 ProcedureReturn result ;If false, there is no guarantee the user can see the window.
EndProcedure

Define event.l,quit.l,x.l,y.l
#Window=1

;Play around with X and Y
x=-2
y=-2

If OpenWindow(#Window,x,y,195,260,"PureBasic Window",#PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_Invisible|#PB_Window_Minimize)

 Debug WindowForceVisible(#Window,2) ;Simple huh? ;) A margin of 2 pixels is used here.

 HideWindow(#Window,#False) 
 Repeat
  event=WaitWindowEvent()
  If event=#PB_Event_CloseWindow  ; If the user has pressed on the close button
   quit=#True
  EndIf
 Until quit
EndIf
End

WindowClipVisible()

Posted: Fri Dec 07, 2007 12:33 am
by Rescator
This is the same as above but improved, it uses the implementation found in the PSDK.
Main difference between this one and the one above is that the window is moved to the nearest desktop edge to where the window was supposed to be.

I also trimmed the code a little, this means that you must make sure the window is not minimized before using WindowClipVisible(), it's a Windows quirk. Whether a window is hidden or not does not matter.
So if you plan to start a program minimized do it this way if you want it flicker free:
1. OpenWindow with invisible flag.
2. use WindowClipVisible() to make sure it's within work area.
3. minimize the window
4. unhide it.

Enjoy folks:

Code: Select all

EnableExplicit

;Checks if the window is in a visible area on screen.
;When is this truly usefull?
;1. When by mistake the x,y position was saved while minimized.
;2. When x,y position saved is wrong.
;3. When the user remove/change/swap multidisplay desktop setup or re-align them.
;4. When the user manages to move to window so far to the side he can't easily grab it
;   and in panic or anger quits/kill the program and try to restart it but the settings
;   was saved with the weird x,y position.
;5. Probably more reasons...Why this behaviour isn't part of the OS is mind-boggling,
;   Would a Open_Viewable window flag been that hard to add to the OS? *laughs*

;If not visible it will attempt to make it visible and return true if it worked.
;False is returned if it fail for some reason, to allow you to handle the error yourself.
;Works fully with Windows 98 or later (multimonitor), there is a fallback for W95 and NT.

;This procedure is best used at program startup, right after you have loaded the program
;settings for example (x,y,minimized or maximized et.al.) and opened the window hidden.

;Tip! To open the window hidden use #PB_Window_Invisible as a window flag
;and HideWindow(#Window,#False) when done making the window, adding gadgets and so on
;Tip! To Restore (un-minimize) a window, use SetWindowState(#Window,#PB_Window_Normal)

Structure MONITORINFO
 cbSize.l
 rcMonitor.RECT
 rcWork.RECT
 dwFlags.l
EndStructure

#MONITOR_DEFAULTTONEAREST=$00000002

Procedure.l WindowClipVisible(window.l)
 Protected result.l=#False,hwnd.l,rect.RECT,hmon.l,mi.MONITORINFO
 Protected noerror.l=#False,x.l,y.l,w.l,h.l
 If IsWindow(window)
  hwnd=WindowID(window)
  If GetWindowRect_(hwnd,rect)
   x=rect\left
   y=rect\top
   w=rect\right-x
   h=rect\bottom-y
   mi\cbSize=SizeOf(MONITORINFO)
   If OSVersion()>=#PB_OS_Windows_98
    hmon=MonitorFromWindow_(hwnd,#MONITOR_DEFAULTTONEAREST)
    If hmon
     noerror=GetMonitorInfo_(hmon,mi)
    Else
     noerror=#False
    EndIf
   Else ;W95,NT3,NT4 fallback.
    noerror=SystemParametersInfo_(#SPI_GETWORKAREA,#Null,mi\rcWork,#Null)
   EndIf
   If noerror
    If (mi\rcWork\right-w)<rect\left : rect\left=mi\rcWork\right-w : EndIf
    If mi\rcWork\left>rect\left : rect\left=mi\rcWork\left : EndIf
    If (mi\rcWork\bottom-h)<rect\top : rect\top=mi\rcWork\bottom-h : EndIf
    If mi\rcWork\top>rect\top : rect\top=mi\rcWork\top : EndIf
    rect\right=rect\left+w
    rect\bottom=rect\top+h
    If (rect\left<>x) Or (rect\top<>y)
     If SetWindowPos_(hwnd,#Null,rect\left,rect\top,#Null,#Null,#SWP_NOACTIVATE|#SWP_NOZORDER|#SWP_NOSIZE)
      result=#True ;Window moved and should now be within the desktop work area.
     EndIf
    Else
     result=#True ;No need to move the window as it is within the work area.
    EndIf
   EndIf
  EndIf
 EndIf
 ProcedureReturn result ;If false, there is no guarantee the user will be able to see the window.
EndProcedure

Define event.l,quit.l,x.l,y.l
#Window=1

;Play around with X and Y
x=-2
y=-2

;If OpenWindow(#Window,x,y,195,260,"PureBasic Window",#PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_Invisible|#PB_Window_Minimize)
If OpenWindow(#Window,x,y,195,260,"PureBasic Window",#PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_Invisible)

 WindowClipVisible(#Window) ;Simple huh? ;)

 HideWindow(#Window,#False) 
 Repeat
  event=WaitWindowEvent()
  If event=#PB_Event_CloseWindow  ; If the user has pressed on the close button
   quit=#True
  EndIf
 Until quit
EndIf
End