Custom Title Bar with Win32 API - Pete - 11-27-2022
So if anyone ever wants to avoid the Windows title bar and create a custom title bar with functions, here's a little demo I whipped up...
It's all SCREEN 0, but you could just as easily make one with a graphics screen.
To drag the window, simply place the mouse pointer on the title bar and hold the left mouse button down, just as you always do for any windows title bar.
The various buttons are clickable. The menu functions are just for show, except for quit.
Code: (Select All) DIM SHARED 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 ShowWindow& (BYVAL hwnd AS LONG, BYVAL nCmdShow 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
DO
winstyle& = GetWindowLongA&(hwnd&, GWL_STYLE)
LOOP UNTIL winstyle&
DO
a& = SetWindowLongA&(hwnd&, GWL_STYLE, winstyle& AND WS_VISIBLE)
LOOP UNTIL a&
REDIM SHARED p1(8), p2(5)
p1(1) = 0 ' Background reg and snooze.
p1(2) = 1 ' Highlight background.
p1(3) = 3 ' Open menu shadow.
p1(4) = 4 ' Show collapsed entries background.
p1(5) = 5 ' Tabs background.
p1(6) = 6 ' Strip between tabs and title bar.
p1(7) = 7 ' Open menu background.
p1(8) = 14 ' Highlight foreground text.
p2(1) = 0 ' Strip between tabs and title bar.
p2(2) = 8 ' Background all pages.
p2(3) = 56 ' Background snooze.
p2(4) = 62 ' Highlight Background.
p2(5) = 63 ' Tabs background.
rt.mrgn = 2: lt.mrgn = 3: tp.mrgn = 4: bt.mrgn = 2
IF lt.mrgn = 0 THEN lt.mrgn = 1 ' Default.
IF tp.mrgn = 0 THEN tp.mrgn = 1 ' Default.
LOCATE 1, 1
PALETTE 5, 63
PALETTE 6, 8
PALETTE 9, 7
PALETTE 7, 7
CALL sam_titlebar
COLOR 15, 6
VIEW PRINT 2 TO _HEIGHT
CLS 2
VIEW PRINT
fw = _FONTWIDTH
fh = _FONTHEIGHT
x = _SCREENX: y = _SCREENY
DO
_LIMIT 60
WHILE _MOUSEINPUT: WEND
mx = _MOUSEX
my = _MOUSEY
' Check pseudo-title bar.
IF my = 1 THEN
' ID by screen character.
IF mx <> tmp% THEN
SELECT CASE CHR$(SCREEN(my, mx))
CASE "X", "þ", "Ä"
IF tmp% THEN COLOR 8, 5: LOCATE my, tmp% - 1: PRINT tmp$;
tmp$ = SPACE$(3): MID$(tmp$, 2, 1) = CHR$(SCREEN(my, mx))
IF MID$(tmp$, 2, 1) = "X" THEN: COLOR 15, 12 ELSE COLOR 15, 7
tmp% = mx: LOCATE my, mx - 1: PRINT tmp$;
CASE "ð", "M", "e", "n", "u" ' Menu.
IF tmp% THEN COLOR 8, 5: LOCATE my, tmp% - 1: PRINT tmp$;
' Exception.
tmp$ = SPACE$(3): MID$(tmp$, 2, 1) = "ð"
tmp% = 2: COLOR 15, 7: LOCATE my, 1: PRINT tmp$;
CASE ELSE
IF tmp% THEN COLOR 8, 5: LOCATE my, tmp% - 1: PRINT tmp$;
tmp% = 0
END SELECT
END IF
ELSE
IF tmp% THEN CALL sam_titlebar: tmp% = 0
END IF
IF GetAsyncKeyState(1) < 0 THEN
IF lb = 0 THEN lb = 1
ELSE
IF lb THEN lb = 0: dragx = 0: dragy = 0
END IF
z& = GetCursorPos(WinMse)
IF lb THEN
IF tmp% THEN
COLOR 8, 5: LOCATE my, tmp% - 1: PRINT tmp$;: tmp% = 0
DO: LOOP UNTIL GetAsyncKeyState(1) = 0: lb = 0
_DELAY .1
SELECT CASE MID$(tmp$, 2, 1)
CASE "X"
SYSTEM
CASE "þ"
IF _FULLSCREEN THEN
_FULLSCREEN OFF
ELSE
_FULLSCREEN
END IF
CASE "Ä"
x& = ShowWindow&(hwnd&, 2)
DO: _LIMIT 1: LOOP UNTIL _SCREENICON = 0
CALL sam_titlebar
CASE "ð"
CALL sam_menu
END SELECT
tmp$ = ""
ELSEIF dragx 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 + dragx, y + dragy)
END IF
z& = GetCursorPos(WinMse)
ELSEIF WinMse.Y_Pos >= _SCREENY AND WinMse.Y_Pos <= _SCREENY + fh THEN
x = _SCREENX: y = _SCREENY
dragx = (WinMse.X_Pos - x)
dragy = fw \ 2 ' Set to middle of the title bar vertical height.
END IF
END IF
IF LEN(INKEY$) THEN SYSTEM
oldypos = WinMse.Y_Pos: oldxpos = WinMse.X_Pos
oldmx = mx: oldmy = my
LOOP
END
SUB sam_titlebar
LOCATE 1, 1
COLOR 0, 5
PRINT SPACE$(_WIDTH);
LOCATE 1, 2: PRINT CHR$(240);
LOCATE , 4: PRINT "Menu";
msg$ = "Sam-Clip"
LOCATE , _WIDTH / 2 - LEN(msg$) / 2 + 1: PRINT msg$;
LOCATE , _WIDTH - 7: PRINT "Ä þ X";
END SUB
SUB sam_menu ' Self-contained subroutine.
y = CSRLIN: x = POS(0)
LOCATE , , 0 ' Hide cursor
clipinsert.var = 0
DIM atmp AS STRING
noi = 6 ' Number of menu items
REDIM menu$(noi)
menu$(1) = "Open"
menu$(2) = "Settings"
menu$(3) = "Recycled"
menu$(4) = "Help"
menu$(5) = "Close"
menu$(6) = "Quit"
h = 5 ' Variable to determine margin spaces from the right of menu.
FOR i = 1 TO noi
j = LEN(menu$(i))
IF j > k THEN k = j
NEXT
mwidth = k + h
mheight = noi * 2 + 1 ' Add one for the separate border element.
MenuT = 1: MenuL = 1: MenuR = MenuL + mwidth: MenuB = MenuT + mheight
DO
_LIMIT 30
z = GetCursorPos(WinMse)
SELECT CASE menu.var
CASE -1
WHILE _MOUSEINPUT: WEND
my = _MOUSEY
mx = _MOUSEX
IF my > MenuT AND my < MenuB AND mx > MenuL AND mx < MenuR THEN
IF my \ 2 = my / 2 AND my AND my <> oldmy THEN
IF MenuHL THEN
atmp = SPACE$(mwidth - 2)
LOCATE MenuHL, MenuL + 2 - 1
COLOR 0, 7
MID$(atmp, 2, LEN(menu$((MenuHL - MenuT) \ 2 + 1))) = menu$((MenuHL - MenuT) \ 2 + 1)
PRINT atmp;
END IF
atmp = SPACE$(mwidth - 2)
LOCATE my, MenuL + 2 - 1
COLOR 7, 0
MID$(atmp, 2, LEN(menu$((my - MenuT) \ 2 + 1))) = menu$((my - MenuT) \ 2 + 1)
PRINT atmp;
COLOR 0, 7
MenuHL = my
END IF
IF _MOUSEBUTTON(1) THEN
menu.var = (my - MenuT) \ 2 + 1
EXIT DO
END IF
ELSE
' Toggle close menu.
IF GetAsyncKeyState(1) < 0 THEN
IF WinMse.Y_Pos >= _SCREENY AND WinMse.Y_Pos <= _SCREENY + 24 AND WinMse.X_Pos >= _SCREENX + 36 AND WinMse.X_Pos <= _SCREENX + 48 THEN
menu.var = 0: EXIT DO ' Close menu.
ELSE
IF WinMse.Y_Pos >= _SCREENY AND WinMse.Y_Pos <= _SCREENY + _FONTHEIGHT * (_HEIGHT + 1) AND WinMse.X_Pos >= _SCREENX AND WinMse.X_Pos <= _SCREENX + _FONTWIDTH * _WIDTH THEN
ELSE ' Outside of app window.
menu.var = 0: EXIT DO ' Close menu.
END IF
END IF
END IF
IF _MOUSEBUTTON(1) THEN ' Outside of menu closes menu.
menu.var = 0 ' Close.
EXIT DO
END IF
END IF
oldmy = my
CASE 0
menu.var = -1 ' Menu open.
PCOPY 0, 1
PALETTE p1(7), p2(5)
PALETTE p1(3), p2(3)
COLOR 0, 7
LOCATE MenuT, MenuL
PRINT CHR$(218) + STRING$(mwidth - 2, 196) + CHR$(191)
FOR i = 1 TO mheight - 2
COLOR 0, 7
PRINT CHR$(179); SPACE$(mwidth - 2) + CHR$(179);
COLOR 7, 3: PRINT CHR$(SCREEN(CSRLIN, POS(0))) + CHR$(SCREEN(CSRLIN, POS(0) + 1)): COLOR 1, 7
NEXT
COLOR 0, 7
PRINT CHR$(192) + STRING$(mwidth - 2, 196) + CHR$(217);: COLOR 7, 3: PRINT CHR$(SCREEN(CSRLIN, POS(0))) + CHR$(SCREEN(CSRLIN, POS(0) + 1))
LOCATE , MenuL + 2
FOR i = 1 TO mheight
PRINT CHR$(SCREEN(CSRLIN, POS(0)));
NEXT
COLOR 0, 7
LOCATE MenuT + 2, MenuL + 2
FOR i = 0 TO noi - 1
LOCATE MenuT + 1 + i * 2, 3
PRINT menu$(i + 1)
LOCATE , MenuL
IF i + 1 < noi THEN PRINT "Ã" + STRING$(mwidth - 2, CHR$(196)) + "´";
NEXT
DO: _LIMIT 10: LOOP UNTIL GetAsyncKeyState(1) = 0 ' Wait for button release to avoid continuous toggle event.
END SELECT
LOOP
PCOPY 1, 0
LOCATE y, x
_KEYCLEAR
IF menu.var = 6 THEN SYSTEM
DO: _LIMIT 10: LOOP UNTIL GetAsyncKeyState(1) = 0
PALETTE 7, 7
END SUB
Pete
RE: Custom Title Bar with Win32 API - mnrvovrfc - 11-27-2022
LOL logging into this forum just now, I got back the regular QB64 Phoenix Edition banner instead of the snowy one.
Anyway, Pete does this let you print any character you want? Because in the other thread it looked like you really wanted to get that three-bar thingie from CHR$(240), I think it was? But I guess that's why this routine could work in a graphics screen... but not your favorite.
It's too bad the SCREEN 0 is limited how it is so that the luddites are satisfied so they don't go forgetting about us and running back to anything that could run 16-bit software.
RE: Custom Title Bar with Win32 API - Dav - 11-27-2022
Nice one, @Pete! I could definitely use this for something. Stashing it away for future use.
- Dav
RE: Custom Title Bar with Win32 API - bplus - 11-27-2022
Wow a menu right off the title bar plus a Title that's centered. Great idea, who needs icons?
Will this work with fonts? and resizing bar to fit a line?
RE: Custom Title Bar with Win32 API - Pete - 11-27-2022
@mnrvovrfc
Quote:Anyway, Pete does this let you print any character you want? Because in the other thread it looked like you really wanted to get that three-bar thingie from CHR$(240), I think it was? But I guess that's why this routine could work in a graphics screen... but not your favorite.
Yes. It removes the restrictions of the Windows title bar and allows ASCII 0-255 and I presume Unicode characters to be included.
A draw back is vertical sizing. A single row title bar is fine for a small window. To keep the text vertically centered would require jumping to 3 rows, which is a bit thick. A way around that is to make the title bar with a hardware overlay. Now you could control the pixel size of the title bar, and even add custom made buttons. Past that is going full graphics. Now with either of those two options I'd have to review the differences in character detection with the SCREEN() statement. I would think it necessary to switch it out with a position detection function to locate the title bar controls.
@Dav
Thanks, I' trying to integrate it right now and switch out the Windows title bar in my Sam-Clip app.
@bplus
Quote:Will this work with fonts? and resizing bar to fit a line?
Yes with fonts. I set the fonts immediately after creating the borderless window. Next add the title bar elements.
Not sure what meant by "resizing bar to fit a line." _RESIZE?
Pete
RE: Custom Title Bar with Win32 API - bplus - 11-27-2022
Quote:Not sure what meant by "resizing bar to fit a line." _RESIZE?
Just using a font at a different height? As I recall Screen 0 could still do different fonts and I assume different heights but might be different through API?
RE: Custom Title Bar with Win32 API - gaslouk - 11-27-2022
I don't know about you but on my own Windows 11 system when running Pete's program when I went to open the top left menu the window was maximized instead.
tmp$ = SPACE$(3): MID$(tmp$, 2, 1) = "Q"
CASE "Q"
So I replaced the "?" of the menu with the letter "Q" and everything works fine now.
Gaslouk.
RE: Custom Title Bar with Win32 API - Pete - 11-27-2022
(11-27-2022, 06:20 PM)gaslouk Wrote: I don't know about you but on my own Windows 11 system when running Pete's program when I went to open the top left menu the window was maximized instead.
tmp$ = SPACE$(3): MID$(tmp$, 2, 1) = "Q"
CASE "Q"
So I replaced the "?" of the menu with the letter "Q" and everything works fine now.
Gaslouk.
Possibly a difference in keyboard language.
Code: (Select All) tmp$ = SPACE$(3): MID$(tmp$, 2, 1) = "ð"
In my QB64 IDE that symbol is converted to ASCII extended character 240, three horizontal bars.
You might want to try a replace with CHR$(240)
Code: (Select All) tmp$ = SPACE$(3): MID$(tmp$, 2, 1) = CHR$(240)
CASE CHR$(240)
RE: Custom Title Bar with Win32 API - Pete - 11-27-2022
(11-27-2022, 06:11 PM)bplus Wrote: Quote:Not sure what meant by "resizing bar to fit a line." _RESIZE?
Just using a font at a different height? As I recall Screen 0 could still do different fonts and I assume different heights but might be different through API?
Yes, you can use whatever TrueType font you want, of any size.
Pete
|