Page 1 of 1

Sizeable & Moveable Borderless-Window, with Snap

Posted: Thu Nov 30, 2017 6:17 pm
by nco2k
if you need a borderless, sizeable window, then #WM_NCCALCSIZE + #WM_NCHITTEST is the recommended way. here are some additional information about modifying and removing the window frame: https://msdn.microsoft.com/en-us/librar ... 85%29.aspx
MSDN wrote:To remove the standard window frame, you must handle the WM_NCCALCSIZE message, specifically when its wParam value is TRUE and the return value is 0. By doing so, your application uses the entire window region as the client area, removing the standard frame.
i made this as a quick/incomplete example. you should adjust the offsets, and take a look at microsofts dwm api. you should also consider doing your own painting. but that would go beyond the scope of what i was trying to demonstrate, with this short example.

also keep in mind that its recommended to create a linker option file, that specifically targets windows vista and higher: /SUBSYSTEM:WINDOWS,6.0. this way windows will not interfere with your size and position, and you will actually get what you actually see.

anyway, i hope it can be of some use. :)

Code: Select all

;Sizeable & Moveable Borderless-Window, with Snap-To-Edge & Aero-Snap by nco2k

EnableExplicit

;adjust to match your skin.
Global BorderWidth = GetSystemMetrics_(#SM_CXSIZEFRAME)
Global BorderHeight = GetSystemMetrics_(#SM_CYSIZEFRAME)
Global TitleBarHeight = GetSystemMetrics_(#SM_CYCAPTION)

;dont set too high or too low. 5-10 is the sweet-spot.
Global SnapWidth = 10
Global SnapHeight = 10

;dont modify!
Global IsSnappedX = #False
Global IsSnappedY = #False

Procedure.w GET_X_LPARAM(lParam)
  ProcedureReturn lParam
EndProcedure

Procedure.w GET_Y_LPARAM(lParam)
  ProcedureReturn lParam >> 16
EndProcedure

Procedure WndProc(hWnd, uMsg, wParam, lParam)
  Protected Result = #PB_ProcessPureBasicEvents, *minmaxinfo.MINMAXINFO, CXSIZEFRAME, CYSIZEFRAME, CYCAPTION, *nccalcsize_params.NCCALCSIZE_PARAMS, WorkAreaRect.RECT, *WindowRect.RECT, WindowRect.RECT, WindowWidth, WindowHeight, MouseX, MouseY, IsCaption, IsTop, IsBottom, IsLeft, IsRight
  
  Select uMsg
    
    Case #WM_GETMINMAXINFO
      
      Result = #False
      
      *minmaxinfo = lParam
      *minmaxinfo\ptMinTrackSize\x = GetSystemMetrics_(#SM_CXMINTRACK)
      *minmaxinfo\ptMinTrackSize\y = BorderHeight * 2 + TitleBarHeight + 1
      
    Case #WM_NCCALCSIZE
      
      Result = #False
      
      If wParam = #True
        
        CXSIZEFRAME = GetSystemMetrics_(#SM_CXSIZEFRAME)
        CYSIZEFRAME = GetSystemMetrics_(#SM_CYSIZEFRAME)
        CYCAPTION = GetSystemMetrics_(#SM_CYCAPTION)
        
        *nccalcsize_params = lParam
        *nccalcsize_params\rgrc[0]\left - CXSIZEFRAME
        *nccalcsize_params\rgrc[0]\top - CYCAPTION - CYSIZEFRAME
        *nccalcsize_params\rgrc[0]\right + CXSIZEFRAME
        *nccalcsize_params\rgrc[0]\bottom + CYSIZEFRAME
        
      EndIf
      
    Case #WM_NCHITTEST
      
      Result = #HTNOWHERE
      
      If GetWindowRect_(hWnd, @WindowRect) And WindowRect\right > WindowRect\left And WindowRect\bottom > WindowRect\top
        
        MouseX = GET_X_LPARAM(lParam) - WindowRect\left
        MouseY = GET_Y_LPARAM(lParam) - WindowRect\top
        
        If MouseX > -1 And MouseY > -1
          
          If MouseY >= BorderHeight And MouseY < TitleBarHeight + BorderHeight And MouseX >= BorderWidth And MouseX < WindowRect\right - WindowRect\left - BorderWidth
            IsCaption = #True
          Else
            If MouseY < BorderHeight
              IsTop = #True
            ElseIf MouseY >= WindowRect\bottom - WindowRect\top - BorderHeight
              IsBottom = #True
            EndIf
            If MouseX < BorderWidth
              IsLeft = #True
            ElseIf MouseX >= WindowRect\right - WindowRect\left - BorderWidth
              IsRight = #True
            EndIf
          EndIf
          
          If IsCaption = #True
            Result = #HTCAPTION
          ElseIf IsTop = #True
            If IsLeft = #True
              Result = #HTTOPLEFT
            ElseIf IsRight = #True
              Result = #HTTOPRIGHT
            Else
              Result = #HTTOP
            EndIf
          ElseIf IsBottom = #True
            If IsLeft = #True
              Result = #HTBOTTOMLEFT
            ElseIf IsRight = #True
              Result = #HTBOTTOMRIGHT
            Else
              Result = #HTBOTTOM
            EndIf
          ElseIf IsLeft = #True
            Result = #HTLEFT
          ElseIf IsRight = #True
            Result = #HTRIGHT
          Else
            Result = #HTCLIENT
          EndIf
          
        EndIf
        
      EndIf
      
    Case #WM_MOVING
      
      Result = #True
      
      If SystemParametersInfo_(#SPI_GETWORKAREA, 0, @WorkAreaRect, 0) And WorkAreaRect\right > WorkAreaRect\left And WorkAreaRect\bottom > WorkAreaRect\top
        
        *WindowRect = lParam
        
        WindowWidth = *WindowRect\right - *WindowRect\left
        WindowHeight = *WindowRect\bottom - *WindowRect\top
        
        If WindowWidth > 0 And WindowHeight > 0
          
          If (IsSnappedX = #False And *WindowRect\left >= WorkAreaRect\left And *WindowRect\left < WorkAreaRect\left + SnapWidth) Or (IsSnappedX = #True And *WindowRect\left = WorkAreaRect\left - 1)
            *WindowRect\left = WorkAreaRect\left
            *WindowRect\right = *WindowRect\left + WindowWidth
            IsSnappedX = #True
          ElseIf (IsSnappedX = #False And *WindowRect\right > WorkAreaRect\right - SnapWidth And *WindowRect\right <= WorkAreaRect\right) Or (IsSnappedX = #True And *WindowRect\right = WorkAreaRect\right + 1)
            *WindowRect\left = WorkAreaRect\right - WindowWidth
            *WindowRect\right = *WindowRect\left + WindowWidth
            IsSnappedX = #True
          ElseIf IsSnappedX = #True
            If *WindowRect\left > WorkAreaRect\left And *WindowRect\left < WorkAreaRect\left + SnapWidth
              *WindowRect\left = WorkAreaRect\left + SnapWidth
              *WindowRect\right = *WindowRect\left + WindowWidth
              IsSnappedX = #False
            ElseIf *WindowRect\right > WorkAreaRect\right - SnapWidth And *WindowRect\right < WorkAreaRect\right
              *WindowRect\left = WorkAreaRect\right - WindowWidth - SnapWidth
              *WindowRect\right = *WindowRect\left + WindowWidth
              IsSnappedX = #False
            EndIf
          EndIf
          
          If (IsSnappedY = #False And *WindowRect\top >= WorkAreaRect\top And *WindowRect\top < WorkAreaRect\top + SnapHeight) Or (IsSnappedY = #True And *WindowRect\top = WorkAreaRect\top - 1)
            *WindowRect\top = WorkAreaRect\top
            *WindowRect\bottom = *WindowRect\top + WindowHeight
            IsSnappedY = #True
          ElseIf (IsSnappedY = #False And *WindowRect\bottom > WorkAreaRect\bottom - SnapHeight And *WindowRect\bottom <= WorkAreaRect\bottom) Or (IsSnappedY = #True And *WindowRect\bottom = WorkAreaRect\bottom + 1)
            *WindowRect\top = WorkAreaRect\bottom - WindowHeight
            *WindowRect\bottom = *WindowRect\top + WindowHeight
            IsSnappedY = #True
          ElseIf IsSnappedY = #True
            If *WindowRect\top > WorkAreaRect\top And *WindowRect\top < WorkAreaRect\top + SnapHeight
              *WindowRect\top = WorkAreaRect\top + SnapHeight
              *WindowRect\bottom = *WindowRect\top + WindowHeight
              IsSnappedY = #False
            ElseIf *WindowRect\bottom > WorkAreaRect\bottom - SnapHeight And *WindowRect\bottom < WorkAreaRect\bottom
              *WindowRect\top = WorkAreaRect\bottom - WindowHeight - SnapHeight
              *WindowRect\bottom = *WindowRect\top + WindowHeight
              IsSnappedY = #False
            EndIf
          EndIf
          
        EndIf
        
      EndIf
      
  EndSelect
  
  ProcedureReturn Result
EndProcedure

OpenWindow(0, 200, 200, 800, 600, "Sizeable & Moveable Borderless-Window", #PB_Window_Invisible | #PB_Window_BorderLess | #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget)

SetWindowCallback(@WndProc(), 0)

SetClassLongPtr_(WindowID(0), #GCL_HBRBACKGROUND, GetStockObject_(#DKGRAY_BRUSH))
SetWindowPos_(WindowID(0), 0, 0, 0, 0, 0, #SWP_NOZORDER | #SWP_NOMOVE | #SWP_NOSIZE | #SWP_NOREDRAW | #SWP_FRAMECHANGED)

HideWindow(0, #False)

Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow : End
edit1: fixed IdeasVacuum's issue?
edit2+3: quick infotext added.

c ya,
nco2k

Re: Sizeable & Moveable Borderless-Window, with Snap

Posted: Thu Nov 30, 2017 8:44 pm
by IdeasVacuum
Win7 x64 PB5.61 x64

Hi nco2k

After a few moves/resizes, the title bar and border appear - is that by design?

Re: Sizeable & Moveable Borderless-Window, with Snap

Posted: Fri Dec 01, 2017 2:46 pm
by RSBasic
Very good. Image

Re: Sizeable & Moveable Borderless-Window, with Snap

Posted: Sat Dec 02, 2017 6:32 am
by ar-s
Thank you :!:

Re: Sizeable & Moveable Borderless-Window, with Snap

Posted: Tue Dec 05, 2017 2:06 pm
by Kwai chang caine
Thanks for sharing 8)

Re: Sizeable & Moveable Borderless-Window, with Snap

Posted: Wed Dec 06, 2017 2:50 am
by IdeasVacuum
So this code is borderless and moveable (and translucent):

Code: Select all

OpenWindow(1,0,0,200,200,"",#PB_Window_BorderLess|#PB_Window_ScreenCentered)

SetWindowColor(1,RGB(0,0,255))

SetWindowLongPtr_(WindowID(1), #GWL_EXSTYLE, GetWindowLongPtr_(WindowID(1), #GWL_EXSTYLE) | #WS_EX_LAYERED)
                                          ;Transparency
SetLayeredWindowAttributes_(WindowID(1),0,156,#LWA_ALPHA)

ButtonGadget(2,0,0,20,20,"X")

Repeat
    Select WaitWindowEvent()
        Case #PB_Event_CloseWindow, #PB_Event_Gadget
            Break
        Case #WM_LBUTTONDOWN
            SendMessage_(WindowID(1), #WM_NCLBUTTONDOWN, #HTCAPTION , #Null)
    EndSelect
ForEver
If the #PB_Window_SizeGadget flag is added, the Window is displayed with borders and title bar. Doesn't that make it a PB bug? :wink:

Re: Sizeable & Moveable Borderless-Window, with Snap

Posted: Wed Dec 06, 2017 3:37 am
by nco2k
creating a simple, borderless and movable window is not a problem. in order to enable the aero-snap functionality, you have to add a sizeable-frame to your window. i already said that you will want to do your own painting, when doing something like this. plus you have aero disabled, which introduces another problem as you can see. windows uses a whole different method of compositing the screen, than it does with aero enabled.

try enabling double buffering and see if it helps:

Code: Select all

SetWindowLongPtr_(WindowID(0), #GWL_EXSTYLE, GetWindowLongPtr_(WindowID(0), #GWL_EXSTYLE) | #WS_EX_COMPOSITED)
but in the long run, you will want to do this properly. the link i provided contains some very valuable information.

c ya,
nco2k

Re: Sizeable & Moveable Borderless-Window, with Snap

Posted: Mon Sep 14, 2020 3:56 pm
by Denis
Hi nco2k,
I've taken a deeper look to your code and finaly i decided to use it in a project i'm on.
Many Thanks for this.
So far, it works perfectly and as I wish. I have modified and simplified it a lot to fit my code.

There is something I'm not able to do, could you explain more and give me an example of linker option file.
nco2k wrote:also keep in mind that its recommended to create a linker option file, that specifically targets windows vista and higher: /SUBSYSTEM:WINDOWS,6.0. this way windows will not interfere with your size and position, and you will actually get what you actually see.

Re: Sizeable & Moveable Borderless-Window, with Snap

Posted: Thu Sep 17, 2020 5:18 pm
by nco2k
in your compiler options, there is a field called linker options file: https://www.purebasic.com/documentation ... ptions.png

create a new text file and add /SUBSYSTEM:WINDOWS,6.0 to it. then select that file as your linker option file.

that will turn this: https://i.imgur.com/pdYXHCF.png

into this: https://i.imgur.com/vmxTkOy.png

both windows have the exact same coordinates, but notice how the borders and close button get cut off in the first pic?

MSFT wrote:
Due to compatability requirements, certain metrics are reported in such a way that they're not consistent with what is actually drawn on the screen when Aero Glass (more accurately, "Windows Vista Aero") is enabled. This sort of approach is needed in order to change the look of the system for the vast majority of apps for which this isn't an issue.

However, there's been a recent change in the system which will be coming out in Vista RC1 that will return the correct rendered value from GetWindowRect() for executables that are linked with "winver = 6.0". This allows new and newly-linked applications to get the "correct" values from GetWindowRect().
c ya,
nco2k

Re: Sizeable & Moveable Borderless-Window, with Snap

Posted: Thu Sep 17, 2020 5:47 pm
by chi
If someone needs a borderless window with shadow... Borderless window with native shadow (Vista+)

Re: Sizeable & Moveable Borderless-Window, with Snap

Posted: Thu Sep 17, 2020 7:50 pm
by nco2k
@chi if you want shadows, all you have to do is put this in my code:

Code: Select all

Structure MARGINS
  cxLeftWidth.l
  cxRightWidth.l
  cyTopHeight.l
  cyBottomHeight.l
EndStructure

Prototype DwmExtendFrameIntoClientArea_(hWnd, *pMarInset)
Global DwmExtendFrameIntoClientArea_.DwmExtendFrameIntoClientArea_

Define DwmapiDLL = OpenLibrary(#PB_Any, "Dwmapi.dll")
If DwmapiDLL
  
  DwmExtendFrameIntoClientArea_ = GetFunction(DwmapiDLL, "DwmExtendFrameIntoClientArea")
  If DwmExtendFrameIntoClientArea_
    
    Define margins.MARGINS
    margins\cyBottomHeight = 1
    
    DwmExtendFrameIntoClientArea_(WindowID(0), @margins)
    
  EndIf
  
  CloseLibrary(DwmapiDLL)
EndIf
but the bottom pixel will glitch out (with your code too btw) and show part of the frame. it will go away once you handle WM_PAINT properly.

c ya,
nco2k

Re: Sizeable & Moveable Borderless-Window, with Snap

Posted: Fri Sep 18, 2020 5:45 am
by Denis
@nco2k

Ok i will try, Thank you.

@chi
I've already seen your code but nco2k's code better suits my needs.
Thank you