Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Think you can do better??? Moving a Borderless Window
#1
This is a mix of WIN32 API and QB64 code. I would have posted it all in Win API, but I haven't looked into controlling the mouse cursor, only locating it. So what it does is produce a borderless window with a fake blank top strip you can drag around the screen. The limits placed on the cursor, to keep it from racing away from the window, are a bit choppy. Maybe that could be improved with a different approach. If anyone has a pure Win API example to compare it to, that would be nice.

Code: (Select All)
DIM WinMse AS POINTAPI
TYPE POINTAPI
    X_Pos AS LONG
    Y_Pos AS LONG
END TYPE

DECLARE DYNAMIC LIBRARY "User32"
    FUNCTION GetWindowLongA& (BYVAL hwnd AS LONG, BYVAL nIndex AS LONG)
    FUNCTION SetWindowLongA& (BYVAL hwnd AS LONG, BYVAL nIndex AS LONG, BYVAL dwNewLong AS LONG)
    FUNCTION SetWindowPos& (BYVAL hwnd AS LONG, BYVAL hWndInsertAfter AS LONG, BYVAL x AS LONG, BYVAL y AS LONG, BYVAL cx AS LONG, BYVAL cy AS LONG, BYVAL wFlags AS LONG)
    FUNCTION GetAsyncKeyState% (BYVAL vkey AS LONG)
    FUNCTION GetCursorPos (lpPoint AS POINTAPI)
END DECLARE

WIDTH 50, 25
DO: LOOP UNTIL _SCREENEXISTS
GWL_STYLE = -16
ws_border = &H800000
WS_VISIBLE = &H10000000
_TITLE "No Border"
hwnd& = _WINDOWHANDLE
winstyle& = GetWindowLongA&(hwnd&, GWL_STYLE)
_DELAY .25
a& = SetWindowLongA&(hwnd&, GWL_STYLE, winstyle& AND WS_VISIBLE)
a& = SetWindowPos&(hwnd&, 0, 0, 200, 400, 0, 39)

LOCATE 1, 1
COLOR 0, 7
PRINT SPACE$(_WIDTH);
fw = _FONTWIDTH
fh = _FONTHEIGHT
x = _SCREENX: y = _SCREENY

DO
    _LIMIT 60

    IF GetAsyncKeyState(1) < 0 THEN
        IF lb = 0 THEN lb = 1
    ELSE
        IF lb THEN lb = 0: dragpt = 0
    END IF

    z = GetCursorPos(WinMse)

    IF lb THEN
        IF dragpt THEN
            IF WinMse.X_Pos <> oldxpos THEN
                j = SGN(WinMse.X_Pos - oldxpos) ' This will be multiplied in statements to speed things up.
                DO
                    x = x + j * 8
                    _SCREENMOVE x, y
                    _MOUSEMOVE dragpt, 1
                    IF j > 0 THEN
                        IF x + dragpt * fw >= WinMse.X_Pos THEN EXIT DO
                    ELSE
                        IF x + dragpt * fw <= WinMse.X_Pos THEN EXIT DO
                    END IF
                LOOP
            END IF
            IF WinMse.Y_Pos <> oldypos THEN
                j = SGN(WinMse.Y_Pos - oldypos)
                DO
                    IF j > 0 THEN
                        y = y + j * 3
                        _SCREENMOVE x, y
                        _MOUSEMOVE dragpt, 1
                        IF y >= WinMse.Y_Pos THEN EXIT DO
                    ELSE
                        y = y + j * 8
                        _SCREENMOVE x, y
                        _MOUSEMOVE dragpt, 1
                        IF y <= WinMse.Y_Pos THEN EXIT DO
                    END IF
                LOOP
            END IF
            z = GetCursorPos(WinMse)
        ELSE
            IF WinMse.Y_Pos >= _SCREENY AND WinMse.Y_Pos <= _SCREENY + fh THEN
                x = _SCREENX: y = _SCREENY
                dragpt = (WinMse.X_Pos - x) \ fw
            END IF
        END IF
    END IF
    IF LEN(INKEY$) THEN SYSTEM
    oldypos = WinMse.Y_Pos
    oldxpos = WinMse.X_Pos
LOOP

Pete
Reply
#2
A different approach:

Code: (Select All)
Declare Dynamic Library "User32"
    Function GetWindowLongA& (ByVal hwnd As Long, Byval nIndex As Long)
    Function SetWindowLongA& (ByVal hwnd As Long, Byval nIndex As Long, Byval dwNewLong As Long)
    Function SetWindowPos& (ByVal hwnd As Long, Byval hWndInsertAfter As Long, Byval x As Long, Byval y As Long, Byval cx As Long, Byval cy As Long, Byval wFlags As Long)
End Declare

Width 50, 25
Do: Loop Until _ScreenExists
GWL_STYLE = -16
ws_border = &H800000
WS_VISIBLE = &H10000000
_Title "No Border"
hwnd& = _WindowHandle
winstyle& = GetWindowLongA&(hwnd&, GWL_STYLE)
a& = SetWindowLongA&(hwnd&, GWL_STYLE, winstyle& And WS_VISIBLE)
a& = SetWindowPos&(hwnd&, 0, 0, 200, 400, 0, 39)



Do
    k = _KeyHit
    x = _ScreenX: y = _ScreenY
    Select Case k
        Case 19200: _ScreenMove x - 10, y
        Case 19712: _ScreenMove x + 10, y
        Case 18432: _ScreenMove x, y - 10
        Case 20480: _ScreenMove x, y + 10
    End Select
    _Limit 30
    Print "Much";
    Print " easier";
    Print " than";
    Print " Pete's";
    Print " method!";
    _Display
Loop Until k = 27


Just use the arrow keys to move your screen.   Tongue

For your mouse, you'd probably want to set it up so it works with either one distinct area of the screen ("click HERE to drag this window!"), or in combo with a control-key.  (CTRL + mouse drag, for instance.)
Reply
#3
Investing in a CHEESE factory, Steve?


ROFL at use the arrow keys. Big Grin Big Grin Big Grin

Pete
Reply
#4
(11-25-2022, 07:45 PM)Pete Wrote: Investing in a CHEESE factory, Steve?


ROFL at use the arrow keys. Big Grin Big Grin Big Grin

Pete

For mouse movement of your borderless window, use my patented MBS function.  It works for all these type need!

Code: (Select All)
Screen _NewImage(800, 600, 32)


Declare Dynamic Library "User32"
    Function GetWindowLongA& (ByVal hwnd As Long, Byval nIndex As Long)
    Function SetWindowLongA& (ByVal hwnd As Long, Byval nIndex As Long, Byval dwNewLong As Long)
    Function SetWindowPos& (ByVal hwnd As Long, Byval hWndInsertAfter As Long, Byval x As Long, Byval y As Long, Byval cx As Long, Byval cy As Long, Byval wFlags As Long)
End Declare

Do: Loop Until _ScreenExists
GWL_STYLE = -16
ws_border = &H800000
WS_VISIBLE = &H10000000
_Title "No Border"
hwnd& = _WindowHandle
winstyle& = GetWindowLongA&(hwnd&, GWL_STYLE)
a& = SetWindowLongA&(hwnd&, GWL_STYLE, winstyle& And WS_VISIBLE)
a& = SetWindowPos&(hwnd&, 0, 0, 200, 400, 0, 39)

Do
    x = _ScreenX: y = _ScreenY
    result = MBS
    If (result And 64) And (_KeyDown(100306) Or _KeyDown(100305)) Then _ScreenMove x + MMX, y + MMY 'ctrl-hold event
    _Limit 60
Loop Until _KeyDown(27)


Function MBS% 'Mouse Button Status
    Static StartTimer As _Float
    Static ButtonDown As Integer
    Static ClickCount As Integer
    Const ClickLimit## = 0.2 'Less than 1/4th of a second to down, up a key to count as a CLICK.
    '                          Down longer counts as a HOLD event.
    Shared Mouse_StartX, Mouse_StartY, Mouse_EndX, Mouse_EndY, MMX, MMY

    MMX = 0: MMY = 0
    While _MouseInput 'Remark out this block, if mouse main input/clear is going to be handled manually in main program.
        Select Case Sgn(_MouseWheel)
            Case 1: tempMBS = tempMBS Or 512
            Case -1: tempMBS = tempMBS Or 1024
        End Select
        MMX = MMX + _MouseMovementX
        MMY = MMY + _MouseMovementY
    Wend


    If _MouseButton(1) Then tempMBS = tempMBS Or 1
    If _MouseButton(2) Then tempMBS = tempMBS Or 2
    If _MouseButton(3) Then tempMBS = tempMBS Or 4


    If StartTimer = 0 Then
        If _MouseButton(1) Then 'If a button is pressed, start the timer to see what it does (click or hold)
            ButtonDown = 1: StartTimer = Timer(0.01)
            Mouse_StartX = _MouseX: Mouse_StartY = _MouseY
        ElseIf _MouseButton(2) Then
            ButtonDown = 2: StartTimer = Timer(0.01)
            Mouse_StartX = _MouseX: Mouse_StartY = _MouseY
        ElseIf _MouseButton(3) Then
            ButtonDown = 3: StartTimer = Timer(0.01)
            Mouse_StartX = _MouseX: Mouse_StartY = _MouseY
        End If
    Else
        BD = ButtonDown Mod 3
        If BD = 0 Then BD = 3
        If Timer(0.01) - StartTimer <= ClickLimit Then 'Button was down, then up, within time limit.  It's a click
            If _MouseButton(BD) = 0 Then tempMBS = 4 * 2 ^ ButtonDown: ButtonDown = 0: StartTimer = 0
        Else
            If _MouseButton(BD) = 0 Then 'hold event has now ended
                tempMBS = 0: ButtonDown = 0: StartTimer = 0
                Mouse_EndX = _MouseX: Mouse_EndY = _MouseY
            Else 'We've now started the hold event
                tempMBS = tempMBS Or 32 * 2 ^ ButtonDown
            End If
        End If
    End If
    MBS = tempMBS
End Function

Now, I don't want any old mouse click and drag to move my screen, so I've set this up to require a CTRL + DRAG event to work.

Press CTRL on the keyboard and hold it.   Press the left mouse button.   Move the mouse, move the window.  

It really is that easy.  No API calls required, except for the ones to hide the title bar and all to begin with.  Wink

Code: (Select All)
Do
    x = _ScreenX: y = _ScreenY
    result = MBS
    If (result And 64) And (_KeyDown(100306) Or _KeyDown(100305)) Then _ScreenMove x + MMX, y + MMY 'ctrl-hold event
    _Limit 60
Loop Until _KeyDown(27)

^ There's my whole program, if you exclude the MBS routine and use it as a library routine, and don't count the code to hide the titlebar.
Reply
#5
(11-25-2022, 07:10 PM)Pete Wrote: This is a mix of WIN32 API and QB64 code. I would have posted it all in Win API, but I haven't looked into controlling the mouse cursor, only locating it. So what it does is produce a borderless window with a fake blank top strip you can drag around the screen. The limits placed on the cursor, to keep it from racing away from the window, are a bit choppy. Maybe that could be improved with a different approach. If anyone has a pure Win API example to compare it to, that would be nice.
@Pete - skillfully again from programming, but where's the practical use?  Huh

Can precision play a role with integer numbers?  That's only interesting for floating point numbers, isn't it?

[Image: Petes-Top-Fenster2022-11-25.jpg]
Reply
#6
(11-25-2022, 09:05 PM)Kernelpanic Wrote: Can precision play a role with integer numbers?  That's only interesting for floating point numbers, isn't it?
"Precision" is just another way of saying bit-width, such as 64-bit having the greatest "precision" you could have out of a whole number in QB64. It seems to be clouded by "SINGLE", "DOUBLE" and "_FLOAT" but the source is the same, how it's interpreted is vastly different from integer. It comes from the size of registers of the CPU.
Reply
#7
(11-25-2022, 09:05 PM)Kernelpanic Wrote:
(11-25-2022, 07:10 PM)Pete Wrote: This is a mix of WIN32 API and QB64 code. I would have posted it all in Win API, but I haven't looked into controlling the mouse cursor, only locating it. So what it does is produce a borderless window with a fake blank top strip you can drag around the screen. The limits placed on the cursor, to keep it from racing away from the window, are a bit choppy. Maybe that could be improved with a different approach. If anyone has a pure Win API example to compare it to, that would be nice.
@Pete - skillfully again from programming, but where's the practical use?  Huh

Can precision play a role with integer numbers?  That's only interesting for floating point numbers, isn't it?

[Image: Petes-Top-Fenster2022-11-25.jpg]

Great question. Say you want to have a custom title bar instead of the Windows title bar wit its predetermined functions like close, restore, minimize, etc. This allows a user to make their custom title bar do whatever they want, while maintaining the best Windows feature, the ability to drag the window around with the mouse.

Pete
Reply
#8
And here's a demo of dragging the window from my custom title bar:

Code: (Select All)
Screen _NewImage(800, 600, 32)
$Color:32

Declare Dynamic Library "User32"
    Function GetWindowLongA& (ByVal hwnd As Long, Byval nIndex As Long)
    Function SetWindowLongA& (ByVal hwnd As Long, Byval nIndex As Long, Byval dwNewLong As Long)
    Function SetWindowPos& (ByVal hwnd As Long, Byval hWndInsertAfter As Long, Byval x As Long, Byval y As Long, Byval cx As Long, Byval cy As Long, Byval wFlags As Long)
End Declare

Do: Loop Until _ScreenExists
GWL_STYLE = -16
ws_border = &H800000
WS_VISIBLE = &H10000000
hwnd& = _WindowHandle
winstyle& = GetWindowLongA&(hwnd&, GWL_STYLE)
a& = SetWindowLongA&(hwnd&, GWL_STYLE, winstyle& And WS_VISIBLE)
a& = SetWindowPos&(hwnd&, 0, 0, 200, 400, 0, 39)

Line (0, 0)-(_Width, 30), SkyBlue, BF 'My custom titlebar
pw = _PrintWidth("ð Steve's Tripple Line Title ð")
_PrintString ((_Width - pw) \ 2, 2), "ð Steve's Tripple Line Tittle ð"

Do
    x = _ScreenX: y = _ScreenY
    result = MBS
    If (result And 64) And (_MouseY <= 30) Then startdrag = -1 '            we're in the title bar and triggered a hold event.
    If startdrag Then
        _ScreenMove x + MMX, y + MMY '                                      drag the window from the title bar
        _MouseMove Mouse_StartX, Mouse_StartY '                              keep the mouse in its original position
    End If
    If (result And 1) = 0 Then startdrag = 0 '                              hold event is over, quit dragging
    _Limit 60
Loop Until _KeyDown(27)


Function MBS% 'Mouse Button Status
    Static StartTimer As _Float
    Static ButtonDown As Integer
    Static ClickCount As Integer
    Const ClickLimit## = 0.2 'Less than 1/4th of a second to down, up a key to count as a CLICK.
    '                          Down longer counts as a HOLD event.
    Shared Mouse_StartX, Mouse_StartY, Mouse_EndX, Mouse_EndY, MMX, MMY

    MMX = 0: MMY = 0
    While _MouseInput 'Remark out this block, if mouse main input/clear is going to be handled manually in main program.
        Select Case Sgn(_MouseWheel)
            Case 1: tempMBS = tempMBS Or 512
            Case -1: tempMBS = tempMBS Or 1024
        End Select
        MMX = MMX + _MouseMovementX
        MMY = MMY + _MouseMovementY
    Wend


    If _MouseButton(1) Then tempMBS = tempMBS Or 1
    If _MouseButton(2) Then tempMBS = tempMBS Or 2
    If _MouseButton(3) Then tempMBS = tempMBS Or 4


    If StartTimer = 0 Then
        If _MouseButton(1) Then 'If a button is pressed, start the timer to see what it does (click or hold)
            ButtonDown = 1: StartTimer = Timer(0.01)
            Mouse_StartX = _MouseX: Mouse_StartY = _MouseY
        ElseIf _MouseButton(2) Then
            ButtonDown = 2: StartTimer = Timer(0.01)
            Mouse_StartX = _MouseX: Mouse_StartY = _MouseY
        ElseIf _MouseButton(3) Then
            ButtonDown = 3: StartTimer = Timer(0.01)
            Mouse_StartX = _MouseX: Mouse_StartY = _MouseY
        End If
    Else
        BD = ButtonDown Mod 3
        If BD = 0 Then BD = 3
        If Timer(0.01) - StartTimer <= ClickLimit Then 'Button was down, then up, within time limit.  It's a click
            If _MouseButton(BD) = 0 Then tempMBS = 4 * 2 ^ ButtonDown: ButtonDown = 0: StartTimer = 0
        Else
            If _MouseButton(BD) = 0 Then 'hold event has now ended
                tempMBS = 0: ButtonDown = 0: StartTimer = 0
                Mouse_EndX = _MouseX: Mouse_EndY = _MouseY
            Else 'We've now started the hold event
                tempMBS = tempMBS Or 32 * 2 ^ ButtonDown
            End If
        End If
    End If
    MBS = tempMBS
End Function
Reply
#9
And the Bud Tugly award goes to...

Getting better.

While the pointer doesn't stay affixed as well, the window movement is smoother when it moves the distance of the difference in the last cursor position.

Code: (Select All)
DIM WinMse AS POINTAPI
TYPE POINTAPI
    X_Pos AS LONG
    Y_Pos AS LONG
END TYPE

DECLARE DYNAMIC LIBRARY "User32"
    FUNCTION GetWindowLongA& (BYVAL hwnd AS LONG, BYVAL nIndex AS LONG)
    FUNCTION SetWindowLongA& (BYVAL hwnd AS LONG, BYVAL nIndex AS LONG, BYVAL dwNewLong AS LONG)
    FUNCTION SetWindowPos& (BYVAL hwnd AS LONG, BYVAL hWndInsertAfter AS LONG, BYVAL x AS LONG, BYVAL y AS LONG, BYVAL cx AS LONG, BYVAL cy AS LONG, BYVAL wFlags AS LONG)
    FUNCTION GetAsyncKeyState% (BYVAL vkey AS LONG)
    FUNCTION GetCursorPos (lpPoint AS POINTAPI)
END DECLARE

WIDTH 50, 25
DO: LOOP UNTIL _SCREENEXISTS
GWL_STYLE = -16
ws_border = &H800000
WS_VISIBLE = &H10000000
_TITLE "No Border"
hwnd& = _WINDOWHANDLE
winstyle& = GetWindowLongA&(hwnd&, GWL_STYLE)
_DELAY .25
a& = SetWindowLongA&(hwnd&, GWL_STYLE, winstyle& AND WS_VISIBLE)
a& = SetWindowPos&(hwnd&, 0, 0, 200, 400, 0, 39)

LOCATE 1, 1
COLOR 0, 7
PRINT SPACE$(_WIDTH);
fw = _FONTWIDTH
fh = _FONTHEIGHT
x = _SCREENX: y = _SCREENY

DO
    _LIMIT 60

    IF GetAsyncKeyState(1) < 0 THEN
        IF lb = 0 THEN lb = 1
    ELSE
        IF lb THEN lb = 0: dragpt = 0
    END IF

    z = GetCursorPos(WinMse)

    IF lb THEN
        IF dragpt THEN
            IF WinMse.X_Pos <> oldxpos OR WinMse.Y_Pos <> oldypos THEN
                j1 = (WinMse.X_Pos - oldxpos)
                j2 = (WinMse.Y_Pos - oldypos)
                x = x + j1: y = y + j2
                _SCREENMOVE x, y
                _MOUSEMOVE dragpt, 1
            END IF
            z = GetCursorPos(WinMse)
        ELSE
            IF WinMse.Y_Pos >= _SCREENY AND WinMse.Y_Pos <= _SCREENY + fh THEN
                x = _SCREENX: y = _SCREENY
                dragpt = (WinMse.X_Pos - x) \ fw
            END IF
        END IF
    END IF
    IF LEN(INKEY$) THEN SYSTEM
    oldypos = WinMse.Y_Pos
    oldxpos = WinMse.X_Pos
LOOP

Pete
Shoot first and shoot people who ask questions, later.
Reply
#10
Okay, just for the hell of it, I figured out how to do a Win32 set mouse cursor event. It really doesn't make any difference.

Code: (Select All)
DIM WinMse AS POINTAPI
TYPE POINTAPI
    X_Pos AS LONG
    Y_Pos AS LONG
END TYPE

DECLARE DYNAMIC LIBRARY "User32"
    FUNCTION GetWindowLongA& (BYVAL hwnd AS LONG, BYVAL nIndex AS LONG)
    FUNCTION SetWindowLongA& (BYVAL hwnd AS LONG, BYVAL nIndex AS LONG, BYVAL dwNewLong AS LONG)
    FUNCTION SetWindowPos& (BYVAL hwnd AS LONG, BYVAL hWndInsertAfter AS LONG, BYVAL x AS LONG, BYVAL y AS LONG, BYVAL cx AS LONG, BYVAL cy AS LONG, BYVAL wFlags AS LONG)
    FUNCTION GetAsyncKeyState% (BYVAL vkey AS LONG)
    FUNCTION GetCursorPos (lpPoint AS POINTAPI)
    FUNCTION SetCursorPos& (BYVAL x AS INTEGER, BYVAL y AS INTEGER)
END DECLARE

DIM AS INTEGER setxy

WIDTH 50, 25
DO: LOOP UNTIL _SCREENEXISTS
GWL_STYLE = -16
ws_border = &H800000
WS_VISIBLE = &H10000000
_TITLE "No Border"
hwnd& = _WINDOWHANDLE
winstyle& = GetWindowLongA&(hwnd&, GWL_STYLE)
_DELAY .25
a& = SetWindowLongA&(hwnd&, GWL_STYLE, winstyle& AND WS_VISIBLE)
a& = SetWindowPos&(hwnd&, 0, 0, 200, 400, 0, 39)

LOCATE 1, 1
COLOR 0, 7
PRINT SPACE$(_WIDTH);
fw = _FONTWIDTH
fh = _FONTHEIGHT
x = _SCREENX: y = _SCREENY

DO
    _LIMIT 60

    IF GetAsyncKeyState(1) < 0 THEN
        IF lb = 0 THEN lb = 1
    ELSE
        IF lb THEN lb = 0: dragpt = 0
    END IF

    z = GetCursorPos(WinMse)

    IF lb THEN
        IF dragpt THEN
            IF WinMse.X_Pos <> oldxpos OR WinMse.Y_Pos <> oldypos THEN
                j1 = (WinMse.X_Pos - oldxpos)
                j2 = (WinMse.Y_Pos - oldypos)
                x = x + j1: y = y + j2
                _SCREENMOVE x, y
                setxy = SetCursorPos(x + dragpt, y)
            END IF
            z = GetCursorPos(WinMse)
        ELSE
            IF WinMse.Y_Pos >= _SCREENY AND WinMse.Y_Pos <= _SCREENY + fh THEN
                x = _SCREENX: y = _SCREENY
                dragpt = (WinMse.X_Pos - x)
            END IF
        END IF
    END IF
    IF LEN(INKEY$) THEN SYSTEM
    oldypos = WinMse.Y_Pos
    oldxpos = WinMse.X_Pos
LOOP

It still beat the heck out of me how we can move a window on the window's desktop with the mouse cursor "fixed" to a drag position but when you try to program that event the mouse cursor isn't fixed, but always trying to readjust.

Pete
Reply




Users browsing this thread: 3 Guest(s)