Sizeable & Moveable Borderless-Window, with Snap

Share your advanced PureBasic knowledge/code with the community.
User avatar
nco2k
Addict
Addict
Posts: 1344
Joined: Mon Sep 15, 2003 5:55 am

Sizeable & Moveable Borderless-Window, with Snap

Post 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
Last edited by nco2k on Wed Dec 06, 2017 12:43 am, edited 3 times in total.
If OSVersion() = #PB_OS_Windows_ME : End : EndIf
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Sizeable & Moveable Borderless-Window, with Snap

Post by IdeasVacuum »

Win7 x64 PB5.61 x64

Hi nco2k

After a few moves/resizes, the title bar and border appear - is that by design?
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
RSBasic
Moderator
Moderator
Posts: 1218
Joined: Thu Dec 31, 2009 11:05 pm
Location: Gernsbach (Germany)
Contact:

Re: Sizeable & Moveable Borderless-Window, with Snap

Post by RSBasic »

Very good. Image
Image
Image
User avatar
ar-s
Enthusiast
Enthusiast
Posts: 340
Joined: Sat Oct 06, 2007 11:20 pm
Location: France

Re: Sizeable & Moveable Borderless-Window, with Snap

Post by ar-s »

Thank you :!:
~Ar-S~
My Image Hoster for PB users
My webSite (french) with PB apps : LDVMULTIMEDIA
PB - 3.x / 5.7x / 6 - W11 x64 - Ryzen 7 3700x / #Rpi4

Code: Select all

r3p347 : 7ry : un71l d0n3 = 1
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Sizeable & Moveable Borderless-Window, with Snap

Post by Kwai chang caine »

Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Sizeable & Moveable Borderless-Window, with Snap

Post 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:
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
nco2k
Addict
Addict
Posts: 1344
Joined: Mon Sep 15, 2003 5:55 am

Re: Sizeable & Moveable Borderless-Window, with Snap

Post 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
If OSVersion() = #PB_OS_Windows_ME : End : EndIf
Denis
Enthusiast
Enthusiast
Posts: 704
Joined: Fri Apr 25, 2003 5:10 pm
Location: Doubs - France

Re: Sizeable & Moveable Borderless-Window, with Snap

Post 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.
A+
Denis
User avatar
nco2k
Addict
Addict
Posts: 1344
Joined: Mon Sep 15, 2003 5:55 am

Re: Sizeable & Moveable Borderless-Window, with Snap

Post 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
If OSVersion() = #PB_OS_Windows_ME : End : EndIf
User avatar
chi
Addict
Addict
Posts: 1028
Joined: Sat May 05, 2007 5:31 pm
Location: Linz, Austria

Re: Sizeable & Moveable Borderless-Window, with Snap

Post by chi »

If someone needs a borderless window with shadow... Borderless window with native shadow (Vista+)
Et cetera is my worst enemy
User avatar
nco2k
Addict
Addict
Posts: 1344
Joined: Mon Sep 15, 2003 5:55 am

Re: Sizeable & Moveable Borderless-Window, with Snap

Post 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
If OSVersion() = #PB_OS_Windows_ME : End : EndIf
Denis
Enthusiast
Enthusiast
Posts: 704
Joined: Fri Apr 25, 2003 5:10 pm
Location: Doubs - France

Re: Sizeable & Moveable Borderless-Window, with Snap

Post 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
A+
Denis
Post Reply