Borderless GUI with non-native resizing

Post your working scripts, libraries and tools for AHK v1.1 and older
evilmanimani
Posts: 29
Joined: 24 Jun 2020, 16:42

Borderless GUI with non-native resizing

04 May 2021, 16:07

I've been working on a program with a custom GUI for a personal project in which I've implemented the method described in the follow old post, in order to have a borderless window which is also resizable; as normally setting both -Caption & +Resize will still leave a thin border.
http://autohotkey.com/board/topic/23969-resizable-window-border/?p=154997

That was all well and good until I began to experience the issue described here a bit further down the same page, where the one message getting missed caused a native-looking border to be drawn over top, which wouldn't go away until you either restarted or minimized/restored it, and it was happening often enough to be annoying.

Lexikos covered the outline of a potential solution in the following reply, which was enough to start with, but I didn't find any working examples online, so I gave it a shot myself.

This took quite a few hours, but hopefully someone finds it useful. You can make some pretty slick looking apps using the handful of tricks shown.
borderless_gui_example.png
borderless_gui_example.png (27.12 KiB) Viewed 1238 times

Code: Select all

#NoEnv
SetBatchLines, -1

OnMessage(0x20, "WM_SETCURSOR")
OnMessage(0x200, "WM_MOUSEMOVE")
OnMessage(0x201, "WM_LBUTTONDOWN")
OnMessage(0x202, "WM_LBUTTONUP")

global gui_id

title := "Borderless Test GUI"
; Normally having -Caption set with +Resize will still show a thin border around the GUI, and a thicker border at the top
; the two values at the end enable doublebuffering, which tends to reduce flicker when updating GUI elements (i.e., resizing)
Gui, +Hwndgui_id -Caption -MaximizeBox +E0x02000000 +E0x00080000 
Gui, Color, black, black
Gui, Font, cEEE8D5 s10 Bold, Segoe UI
Gui, Add, Text, xm, % title
Gui, Font, s12 Norm, Segoe MDL2 Assets
Gui, Add, Text, yp vminimizeBtn 0x100, % Chr(0xE921)
Gui, Add, Text, yp vcloseBtn gGuiClose, % Chr(0xE106)
Gui, Font, s1
Gui, Add, Text, y+m vtitlebarDiv x1 +0x5 ; useful as a titlebar divider
Gui, Font, s9, Segoe UI
Gui, Add, Text, xm y+m, Here's some test text
Gui, Add, Edit, w300 h300, Here's an edit control
Gui, Show, Autosize, % title
WinSet, Transparent, 200, % "ahk_id" gui_id 
Return

GuiClose:
ExitApp

; Resizes the edit control and titlebar div, moves the 
; minimize & close buttons to the right of the GUI
;
; Set to Critical to prevent an issue where sometimes
; part of the window can disappear if it sets the window
; region before all the controls are able to be drawn
; but it might slow things down if you're using a lot
; of controls with AutoXYWH() for instance.
GuiSize:
    Critical
    If ErrorLevel = 1
        Return
    WinGetPos, , , winW, winH, % "ahk_id" gui_id
    Guicontrol, Move, titlebarDiv, % "w" winW
    GuiControl, MoveDraw, minimizeBtn, % "x" winW - 55
    GuiControl, MoveDraw, closeBtn, % "x" winW - 30
    GuiControl, Move, Edit1, % "w" winW - 25 " h" winH - 80
    WinSet, Region , % "0-0 R15-15 w" winW " h" winH
Return

WM_LBUTTONUP() {
    global
    If isResizing {
        isResizing := 0
        DllCall("ReleaseCapture")
        Tooltip
    }
}

WM_LBUTTONDOWN() {

    global isResizing, resizeBorder
    WinGetPos, winX, winY, winW, winH, % "ahk_id" gui_id
    MouseGetPos, mouseX, mouseY

    ; Capture mouse if cursor is over the resize area
    If (!isResizing && resizeBorder) {
        isResizing := 1
        DllCall("SetCapture", "UInt", gui_id)
    }

    If ( A_Gui && mouseY < 29 ) ; move window from titlebar area while menus are hidden
        PostMessage, 0xA1, 2 ; 0xA1 = WM_NCLBUTTONDOWN

    If (A_GuiControl = "minimizeBtn") ; Minimize whent the 'button' is clicked
        WinMinimize, % "ahk_id" gui_id

}

WM_SETCURSOR() {

    global resizeBorder
    WinGetPos, , , winWidth, winHeight, % "ahk_id" gui_id
    borderSize      := 8
    , cornerMargin  := 10
    , borderW       := borderSize
    , borderE       := winWidth - borderSize
    , borderN       := borderSize
    , borderS       := winHeight - borderSize    
    , IDC_SIZENS    := 32645
    , IDC_SIZEWE    := 32644
    , IDC_SIZENWSE  := 32642
    , IDC_SIZENESW  := 32643
    
    CoordMode, Mouse, Client
    MouseGetPos, mouseX, mouseY
    
    Switch
    {
        Case (mouseX < borderW + cornerMargin && mouseY < borderN + cornerMargin) : Cursor := IDC_SIZENWSE, resizeBorder := "NW"
        Case (mouseX < borderW + cornerMargin && mouseY > borderS - cornerMargin) : Cursor := IDC_SIZENESW, resizeBorder := "SW"
        Case (mouseX > borderE - cornerMargin && mouseY < borderN + cornerMargin) : Cursor := IDC_SIZENESW, resizeBorder := "NE"
        Case (mouseX > borderE - cornerMargin && mouseY > borderS - cornerMargin) : Cursor := IDC_SIZENWSE, resizeBorder := "SE"
        Case (mouseX < borderW) : Cursor := IDC_SIZEWE, resizeBorder := "W"
        Case (mouseX > borderE) : Cursor := IDC_SIZEWE, resizeBorder := "E"
        Case (mouseY > borderS) : Cursor := IDC_SIZENS, resizeBorder := "S"
        Case (mouseY < borderN) : Cursor := IDC_SIZENS, resizeBorder := "N"
        Default: Cursor := "", resizeBorder := ""
    }
    If (Cursor) {
        CursorHandle := DllCall("LoadCursor", "ptr", 0, "ptr", Cursor, "ptr")
        LastCursor := DllCall("SetCursor", "uint", CursorHandle)
        Return true
    } Else If (CursorHandle <> LastCursor) {
        CursorHandle := DllCall("SetCursor", "uint", LastCursor)
        Return true
    } Else return
    
}

WM_MOUSEMOVE() {

    global resizeBorder, isResizing
    WinGetPos, winX, winY, winWidth, winHeight, % "ahk_id" gui_id
    CoordMode, Mouse, Screen
    MouseGetPos, mouseX, mouseY
    minWidth        := 200
    , minHeight     := 150

    If isResizing {

        winSYPos := winY + winHeight
        winEXPos := winX + winWidth

        If InStr(resizeBorder, "W") {
            newWidth := winEXPos - mouseX
            If (newWidth > minWidth && mouseX > 0)
                SetWindowPosition(gui_id,mouseX,,newWidth)
        } Else If InStr(resizeBorder, "E") {
            newWidth := mouseX - winX
            If (newWidth > minWidth)
                SetWindowPosition(gui_id,,,newWidth)
        }

        If InStr(resizeBorder, "N") {
            newHeight := winSYPos - mouseY
            If (newHeight > minHeight && mouseY > 0)
                SetWindowPosition(gui_id,,mouseY,,newHeight)
        } Else If InStr(resizeBorder, "S") {
            newHeight := mouseY - winY
            If (newHeight > minHeight)
                SetWindowPosition(gui_id,,,,newHeight)
        }
        
    }
}

SetWindowPosition(hwnd, x := "", y := "", w := "", h := "") {
    WinGetPos, winX, winY, winW, winH, % "ahk_id" hwnd
    x := x ? x : winX   ; Set vars to found positions if params are blank
    , y := y ? y : winY
    , w := w ? w : winW
    , h := h ? h : winH
    DllCall("SetWindowPos","uint",hwnd,"uint",0
    ,"int",x,"int",y,"int",w,"int",h
    ,"uint",0x40)
    ToolTip, % "x" x " y" y " w" w " h" h
}
Last edited by evilmanimani on 05 May 2021, 00:35, edited 1 time in total.
iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Re: Borderless GUI with non-native resizing

04 May 2021, 21:16

Perhaps you should be capturing WM_SETCURSOR?

I appreciate the use of my modified script, but you should be using SetCursor because you have a GUI window. SetSystemCursor is a global function that affects all of windows. (I actually re-read my old code and found some bugs which were fixed!)
evilmanimani
Posts: 29
Joined: 24 Jun 2020, 16:42

Re: Borderless GUI with non-native resizing

05 May 2021, 00:37

Good suggestions, I was able to do exactly that, and was able to excise your function, since it looks like it wasn't really needed in this particular case :D

I also got diagonals working, so that's neat, you can adjust the size of the corners with the 'cornerMargin' variable, I've updated the OP.
iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Re: Borderless GUI with non-native resizing

05 May 2021, 13:10

I see some heavy flickering when resizing however. I also added -DPIScale to have it display correctly on my laptop.
iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Re: Borderless GUI with non-native resizing

05 May 2021, 13:52

It's probably CS_VREDRAW and CS_VREDRAW.
http://www.catch22.net/tuts/win32/flicker-free-drawing# wrote:
When you just can’t help it
There are occasions when you spend alot of time and effort getting your super-duper drawing code working, only to find that your window is still getting redrawn in it’s entirety. This is usually the cause of two window class styles - CS_VREDRAW and CS_HREDRAW. When a window class has either of these two styles set, the window contents will be completely redrawn every time it is resized either vertically or horizontally (or both). So, you need to turn off these two class styles. The only way to do this is to make sure your window isn’t created with them in the first place, and to prevent this from happening, you have to make sure that CS_HREDRAW and CS_VREDRAW aren’t included when the window class is registered.

Code: Select all

WNDCLASSEX wc;

wc.cbSize = sizeof(wc);

wc.style = 0; /* CS_VREDRAW | CS_VREDRAW ; */

...

RegisterClassEx(&wc);
The above example is just to help illustrate the point that these two styles must not be included when the window class is registered.

Just a word of warning here: If the main window in an application has these two class styles set, then this will cause all child windows to be redrawn during a resize, even if those children don’t have the redraw flags set. This can be avoided by following the next step:
I can't get a custom window from CreateWindow to play nice with AHK Gui, %hwnd% commands, so this is likely a dead end.
MsgBox wrote: Error: Invalid Gui name.

Specifically: 7865268:Default

Line#
004: OnMessage(0x200, "WM_MOUSEMOVE")
005: OnMessage(0x201, "WM_LBUTTONDOWN")
006: OnMessage(0x202, "WM_LBUTTONUP")
010: title := "Borderless Test GUI"
015: UnRegisterClass("CustomClassName")
016: MsgBox,RegisterClass("CustomClassName")
017: MsgBox,gui_id := CreateWindow(title,,,A_ScriptHwnd)
---> 018: Gui,%gui_id%:Default
019: Gui,Color,black,black
020: Gui,Font,cEEE8D5 s10 Bold,Segoe UI
021: Gui,Add,Text,xm,title
022: Gui,Font,s12 Norm,Segoe MDL2 Assets
023: Gui,Add,Text,yp vminimizeBtn 0x100,Chr(0xE921)
024: Gui,Add,Text,yp vcloseBtn gGuiClose,Chr(0xE106)
025: Gui,Font,s1

The current thread will exit.
AHKStudent
Posts: 1472
Joined: 05 May 2018, 12:23

Re: Borderless GUI with non-native resizing

05 May 2021, 14:19

really nice work, lots of potential. I wish one day ahk can have easy to use controls that don't look like the ancient windows controls. Such a pretty gui looks sad when you throw those ancient edit etc controls on it

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 80 guests