The MASM Forum

General => The Workshop => Windows API => Topic started by: NoCforMe on November 05, 2023, 11:15:28 AM

Title: MyToolstrip: A possible replacement for the Windows toolbar
Post by: NoCforMe on November 05, 2023, 11:15:28 AM
I got tired of dealing with the idiosyncrasies, limitations and problems with the Win32 toolbar, so I decided to roll my own. It was a lot of work, but I think it's been worth it.

Note: This is not a finished project! Still under construction. However, this is a working demo that I wanted to get out there so people could play with it and maybe offer some feedback, good or bad. It's actually working well enough for me to put it into one of my programs.

For now I'm just posting this executable. Later I'll be happy to make the full source available for anyone interested. It will be in the form of a single assembly-language module that could be assembled and linked to a program. I could make a DLL later if that seems appropriate.

Anyhow, here's what it's all about:

The toolstrip allows you to put any of several of the standard Windows controls within it, as well as "TS-type" buttons which are basically bitmaps that function as buttons. The standard Windows controls can be any of the following:

Actually, there's nothing stopping you from putting any type of Windows control in there, like a listbox, etc. It's just that anything that's taller than a single line won't work very well in a narrow strip. I haven't tried it but I'm sure a combobox would work OK.

You add Windows controls by giving the following info., in a structure (TSELEMENT):

Notice that there's no width parameter required: the width of the control is computed for you from the length (GDI length, not # of characters) of the text. I think this is a really nice feature; the controls are automatically sized and spaced in the toolbar. Even if the control has no text displayed--say it's an output field, either a static or edit control--you supply a string of text which is used to size it.

For "TS-type" (non-Windows) buttons, you have to specify:

TS-type buttons can be either individual ones or a group of buttons that behaves like a set of radio buttons; push one and it stays selected, push another and it becomes selected. This works like the BTNS_GROUP style for Windows toolbar buttons. (The demo has two groups of buttons so you can see how they operate.)

** I'm going to change this so the actual size of the bitmap is used to size it.

You can also add tooltips, as in the demo. These took a hell of a lot of work to get working as well as they do, which isn't perfect. But I must say it's a hell of a lot better than the Windows tooltips. I remember Steve (Hutch) used to rail against Win32 tooltips; he hated them, and for good reason. They're super-flaky! At first I tried to implement them using the recommended interface, which is to use TrackMouseEvent() to tell when the user hovers over a control with the mouse. I got this to work fairly well, but it was very unreliable: often you could move the mouse over a button and nothing would happen, or it would take f-o-r-e-v-e-r for the WM_MOUSEHOVER message to arrive so you could put up your tooltip.

I just gave up on that method (I still use TrackMouseEvent() to give me a WM_MOUSELEAVE message so I can know when the user has moved off a button, at which point I get rid of the tooltip windows). My method is so much simpler, and it seems to work OK (well, about 98% of the time): I simply look at all WM_MOUSEMOVE messages coming from my buttons and do hit tests on them to figure out when the mouse pointer is over one of them. If that happens, I put up the tooltip windows. (There are actually 2 windows, the little box with text in it and an underlying "shadow" window just for looks.)

I found in working on this that a lot of the work is just finessing stuff to make it work smoothly. Now, my method, being simpler than Windows, lacks some of the fine points, like a short delay in putting up the tooltip, but overall it's just so much more reliable. I never miss a tooltip. There are some cases where the tooltip stays up when it shouldn't (and I would really like to hear from you if you find this happening in any specific way). But even this isn't a show-stopper, just a minor annoyance. I found that adding a 50mS delay after creating the windows eliminated a lot of display problems, like a jittery oscillation if you landed between two buttons. There are a lot of similar tweaks in the code, mostly small positioning adjustments.

I checked, and there are no leaks, at least no GDI leaks. Shouldn't be any memory leaks either since I'm not allocating any memory here.

The box in the middle shows another toolstrip, this one vertical and sized to its contents. I don't have all the vertical-toolstrip stuff functional yet, but they do work.

Coding to use my toolstrips take a bit more work on the data side, not much, but you have to fill in a TSELEMENT structure for each button. Instead of having all the bitmap buttons in a strip like Win32 does, each button here is an individual bitmap, so there's a little more overhead, but not much. You don't have to send any special message to load the bitmaps: that's done automagically through the element list when you call the toolstrip-element creation function, CreateToolstripContent(). Oh, and notice that I can handle bitmap buttons that aren't square (what a concept!).

Anyhow, play with it and let me know what you think.
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: Biterider on November 05, 2023, 05:43:14 PM
Hi NoCforMe
I gave it a quick shot. Nice project!  :thup:
What is your motivation? Is the MS control not good enough?

When running, I noticed 2 things.
First, an intense flickering when moving the mouse. 
The other one is more of a cosmetic issue. When a tooltip is displayed and you move the main window, the tooltip stays where it is. A more consistent behaviour would be to hide it immediately when the parent window changes position.

Regards, Biterider
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: NoCforMe on November 05, 2023, 06:18:32 PM
Quote from: Biterider on November 05, 2023, 05:43:14 PMHi NoCforMe
I gave it a quick shot. Nice project!  :thup:
What is your motivation? Is the MS control not good enough?

Well, as I said, the MS toolbar has problems and limitations. Plus it's a pain in the ass to program. This version is more flexible in some ways.

QuoteWhen running, I noticed 2 things.
First, an intense flickering when moving the mouse.
The other one is more of a cosmetic issue. When a tooltip is displayed and you move the main window, the tooltip stays where it is. A more consistent behaviour would be to hide it immediately when the parent window changes position.

Yes, I just discovered that myself. Your reply gives me an idea of how to fix it, maybe: destroy the tooltip windows upon receipt of a WM_MOVE message. (BTW, the fact that you were able to move the window with the tooltip visible shouldn't have been possible: they're supposed to go away as soon as the mouse moves away from the button that triggered the tooltip in the first place.)

Flickering? That's not good. I don't see any myself. What OS are you using?

I do see times when the tooltips stay up even after mousing away from the button that brought them up, which isn't right. Still working on it.
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: Biterider on November 05, 2023, 06:48:48 PM
Quote from: NoCforMe on November 05, 2023, 06:18:32 PMFlickering? That's not good. I don't see any myself. What OS are you using?
Windows 10 Home, 22H2

Regards, Biterider
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: jj2007 on November 05, 2023, 08:21:17 PM
Looks good :thumbsup:

A bit of flicker here on Win7-64, but nothing to worry about. Tooltips look non-standard, I have not yet decided whether I like it or no.

No source yet, ok, but let me ask: what's the interface for designing the toolbar? Something like the Excel table in the editor thread (https://masm32.com/board/index.php?topic=5858.0), 7 years ago?

> I remember Steve (Hutch) used to rail against Win32 tooltips; he hated them, and for good reason
Yes, in that same thread Steve argued quite a bit ;-)
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: TimoVJL on November 05, 2023, 09:21:36 PM
Also a toolbar menu could be space saver.
(https://i.postimg.cc/9D93tdtd/ToolMenu.png) (https://postimg.cc/9D93tdtd)
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: jj2007 on November 05, 2023, 09:41:15 PM
I use TLPEView often, Timo. How did you insert the menu into the toolbar?
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: TimoVJL on November 05, 2023, 09:46:59 PM
My bad, wrong example code.
EDIT: TBMenu code
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: jj2007 on November 05, 2023, 10:10:42 PM
Quote from: TimoVJL on November 05, 2023, 09:46:59 PMShort example:

We are talking about different things:
- to the left, TLPEView opens a standard menu when hovering over the toolbar - the menu is integrated;
- to the right, what you posted right now: a menu above the toolbar; and it opens only when you click on it.

I'd like to know how you did version 1 - I like it :thumbsup:
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: jj2007 on November 06, 2023, 12:37:12 AM
Quote from: NoCforMe on November 05, 2023, 11:15:28 AMThe toolstrip allows you to put any of several of the standard Windows controls within it

It took you a while (https://www.masmforum.com/board/index.php?topic=17788.0) ;-)
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: NoCforMe on November 06, 2023, 03:58:57 AM
Well, what can I say? My wheels grind slow, but they grind fine.

So about that flickering (which I now see, when I move the mouse rapidly over the toolstrip): I'm pretty sure I know why it happens. Don't know how to fix it, though. Here's what's happening:

When I create those 2 little tooltip windows, they become activated, meaning that the windows behind them--the main application window and its contents--become deactivated, meaning their borders get repainted and all that. Which I don't want.

I tried using WS_EX_NOACTIVATE when creating the tooltip windows: that doesn't work. Tried using SWP_NOACTIVATE with SetWindowPos() when I resize and reposition these windows: that has no effect either.

The one thing that did work was to put a WM_ACTIVATE handler in the tooltip window proc:
    CMP    uMsg, WM_ACTIVATE
    JE    do_activate
.....
do_activate:
    CMP    WORD PTR wParam, WA_INACTIVE
    JE    dodefault
    INVOKE    SetActiveWindow, lParam
    XOR    EAX, EAX
    RET

so that every time the tooltip windows get activated, they turn right around and re-activate the previously-activated window (the one whose handle is in lParam). It works, but I'm pretty sure that's where all that flicker is coming from.

I did a pretty extensive online search for solutions to this problem, on StackOverflow and friends; found a lot of discussions of it, but no real definitive solutions.

Does anyone here know how I can create a window and not have it take over activation?

All this makes me jealous of MS developers, who have access to all the hidden innards of the OS and can easily solve problems like this with all kinds of undocumented stuff. Us peons on the outside have to root around in the published interfaces for work-arounds ...
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: NoCforMe on November 06, 2023, 04:07:02 AM
Quote from: jj2007 on November 05, 2023, 08:21:17 PMA bit of flicker here on Win7-64, but nothing to worry about. Tooltips look non-standard, I have not yet decided whether I like it or no.
Please do let me know the verdict. I'm interested in people's reactions to this admittedly non-standard control.
QuoteNo source yet, ok, but let me ask: what's the interface for designing the toolbar? Something like the Excel table in the editor thread (https://masm32.com/board/index.php?topic=5858.0), 7 years ago?
Interface? Interface? We don't need no steenkin' interface!

No design interface; the toolstrips are constructed with an array of structures:
TSELEMENT <$tsBtnGroup, 0, 0, $mainTsTsBtnWidth, $mainTsTsBtnHeight, $TBbutton1, 500, Btn1text>
TSELEMENT <$tsBtnGroup, 0, 0, $mainTsTsBtnWidth, $mainTsTsBtnHeight, $TBbutton2, 501, Btn2text>
TSELEMENT <$tsBtnGroup, 0, 0, $mainTsTsBtnWidth, $mainTsTsBtnHeight, $TBbutton3, 502, Btn3text>
TSELEMENT <$tsSep>
TSELEMENT <$tsButton, 0, 0, $mainTsTsBtnWidth, $mainTsTsBtnHeight, $TBbutton4, 501, Btn4text>
TSELEMENT <$tsSep>
TSELEMENT <$tsBtnGroup, 0, 0, $mainTsTsBtnWidth, 14, $TBbutton5, 503, Btn5text>
TSELEMENT <$tsBtnGroup, 0, 0, $mainTsTsBtnWidth, 14, $TBbutton6, 504, Btn6text>
TSELEMENT <$tsBtnGroup, 0, 0, 24, 14, $TBbutton7, 505, Btn8text>
TSELEMENT <$tsSep>
TSELEMENT <0,        ButtonClassname, $buttonStyles, BTNxtext, $mainTsBtnHeight, $TBbutton8, 0, Btn9text>
TSELEMENT <$tsNoText,    StaticClassname, $staticStyles, ST1OutputText, $tsStaticHeight, $ST2>
    DD -1
I've set up all the colors, line widths, element spacing, etc., by hand using EQUates:
$tsElementSpacing EQU 2
$tsButtonSpacing EQU 4
$TSbtnBtnOffset EQU 4
$TSselBoxMargin EQU 2
$tsVelementSpacing EQU 1
$tsElementPadding EQU 6
$tsSepWidth EQU 4
$tsSepHeight EQU 3
$tsStaticHeight EQU 14
$tsEditHeight EQU 18
$tsBtnPushedXoffset EQU 1
$tsBtnPushedYoffset EQU 1
$tsCheckboxAddon EQU 16
$tsSepPenWidth EQU 1
$tsSepPenHlWidth EQU 1
$tsSepPenColor EQU $colorGray
$tsSepPenHlColor EQU $colorWhite
$tsBackgroundColor EQU $colorVltBlue

Source coming soon, after some dust settles ...
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: Greenhorn on November 06, 2023, 07:39:45 AM
Quote from: jj2007 on November 05, 2023, 10:10:42 PM
Quote from: TimoVJL on November 05, 2023, 09:46:59 PMShort example:

We are talking about different things:
- to the left, TLPEView opens a standard menu when hovering over the toolbar - the menu is integrated;
- to the right, what you posted right now: a menu above the toolbar; and it opens only when you click on it.

I'd like to know how you did version 1 - I like it :thumbsup:

How to Create an Internet Explorer-Style Menu Bar (https://learn.microsoft.com/en-us/windows/win32/controls/cc-faq-iemenubar)
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: Greenhorn on November 06, 2023, 07:45:32 AM
QuoteI got tired of dealing with the idiosyncrasies, limitations and problems with the Win32 toolbar, so I decided to roll my own. It was a lot of work, but I think it's been worth it.

Looks good.
I did a similar project a while ago, a clone of the Ms Office 2003 Commandbars.

And yes, if you want to implement it properly it's a lot of work and headaches.
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: jj2007 on November 06, 2023, 07:48:45 AM
Quote from: Greenhorn on November 06, 2023, 07:39:45 AMHow to Create an Internet Explorer-Style Menu Bar

Interesting, thanks, but not what Timo did in his TLPEView, where the menu is at the same level as the toolbar, not above. It seems, though, that there are two toolbars displayed, so maybe it can work. The M$ site does not give an example, unfortunately. This will require some time and effort...

(https://learn.microsoft.com/en-us/windows/win32/controls/images/howto8.jpg)
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: Greenhorn on November 06, 2023, 08:04:49 AM
Quote from: jj2007 on November 06, 2023, 07:48:45 AM
Quote from: Greenhorn on November 06, 2023, 07:39:45 AMHow to Create an Internet Explorer-Style Menu Bar

Interesting, thanks, but not what Timo did in his TLPEView, where the menu is at the same level as the toolbar, not above. It seems, though, that there are two toolbars displayed, so maybe it can work. The M$ site does not give an example, unfortunately. This will require some time and effort...

There is no need to use a Rebar and nothing prevents you to add different toolbar buttons to your "Menu" toolbar. And yes, it will take a little time and effort to implement it.

EDIT:
Another way is to use Rebar controls without a gripper and then place a second Rebar right next to the "Menu" toolbar.
This way you can use TBSTYLE_FLAT with the "Menu" toolbar and for the regular buttons in the other toolbar the default style.
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: NoCforMe on November 06, 2023, 09:26:15 AM
Quote from: Greenhorn on November 06, 2023, 08:04:49 AMThere is no need to use a Rebar and nothing prevents you to add different toolbar buttons to your "Menu" toolbar.

So are you talking about something like this? It's a menu bar (strip, whatever). I just dropped a bunch of bitmap buttons into it like so:

    INVOKE    AppendMenu, MenuHandle, MF_BITMAP, $menuUpcase, bmp1Handle
    INVOKE    AppendMenu, MenuHandle, MF_BITMAP, $menuDowncase, bmp2Handle
    INVOKE    AppendMenu, MenuHandle, MF_BITMAP, $menuToTop, bmp3Handle
    INVOKE    AppendMenu, MenuHandle, MF_BITMAP, $menuToEnd, bmp4Handle
    INVOKE    AppendMenu, MenuHandle, MF_BITMAP, $menuBoing, bmp6Handle
    INVOKE    AppendMenu, MenuHandle, MF_BITMAP, $menuViewExtraFile, bmp5Handle

Easy peasy. Unless I'm mistaken; apologies if I've misunderstood what you're after (well, JJ) here.

I wonder if you could somehow add child window controls to the menu bar?
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: Greenhorn on November 06, 2023, 09:35:38 AM
Quote from: NoCforMe on November 06, 2023, 09:26:15 AMSo are you talking about something like this? It's a menu bar (strip, whatever). I just dropped a bunch of bitmap buttons into it like so:

No, it's about using Win32 toolbar control acting as a menu bar. Read the link I've posted. It describes what to do to make a toolbar acting as a menu bar, in this case placing the toolbar into a Rebar control, like Iexplorer does/did.

However, thread is drifting away ...
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: jj2007 on November 06, 2023, 11:25:53 AM
This is a PaintShopped demo of what I am after (and it's actually what Timo does in TLPEView), i.e. the menu inside the toolbar, not above. Thus, I can gain one line of vertical space.
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: TimoVJL on November 06, 2023, 11:55:29 AM
Short example
// WSDIFrameTBMenu4.c
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <commctrl.h>

#pragma comment(lib, "comctl32.lib")

#define IDM_EXIT 6001
#define IDM_NEW  6002
#define IDM_OPEN 6003

#define ID_MENUF 7000
#define ID_MENUE 7001
#define ID_MENUT 7002

#define IDC_STATUS  4000
#define IDC_TOOLBAR 4001

#ifndef NELEMS
#define NELEMS(a)  (sizeof(a) / sizeof(a[0]))
#endif

static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
static void OnDestroy(HWND hwnd);
static void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
static void OnSize(HWND hwnd, UINT state, int cx, int cy);
static void OnNotify(HWND hwnd, int idCtrl, NMHDR *pNMHdr);
static void OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);
static void OnEnterIdle(HWND hwnd, UINT source, HWND hwndSource);
static void OnSysKey(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags);
static void TBContextMenu(HWND hwnd, NMTOOLBAR *pNMTB, int iItem);
//static void TBContextMenu(HWND hwnd, NMTOOLBAR *pNMTB);

static TCHAR szAppName[] = TEXT("WSDIFrameTBMenu");
static TCHAR szFrameClass[] = TEXT("cWSDIFrame");
static HWND g_hFrame, g_hStatus, g_hToolbar;
static HANDLE g_hInst;
static HMENU g_hMenu;

#ifdef UNICODE
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                LPWSTR lpCmdLine, int nCmdShow)
{
#else
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                LPSTR lpCmdLine, int nCmdShow)
{
#endif // UNICODE
    WNDCLASSEX wcx;
    MSG msg;

    wcx.cbSize = sizeof(WNDCLASSEX);
    GetClassInfoEx(hInstance, MAKEINTRESOURCE(32770), &wcx);
    wcx.lpfnWndProc = (WNDPROC)WndProc;
    wcx.hInstance = hInstance;
    wcx.hbrBackground = (HBRUSH)COLOR_3DSHADOW;
    wcx.lpszClassName = szFrameClass;

    if (!RegisterClassEx(&wcx))
        return 0;
    g_hInst = hInstance;

    g_hFrame = CreateWindowEx(0, szFrameClass, szAppName,
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
                NULL, NULL, hInstance, NULL);
    if (!g_hFrame)
        return 0;
    ShowWindow(g_hFrame, nCmdShow);
    UpdateWindow(g_hFrame);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

static HWND MakeToolbar(HWND hWnd, UINT wID)
{
    TBBUTTON tbb[] = {
        {-1, ID_MENUF, TBSTATE_ENABLED, TBSTYLE_DROPDOWN|BTNS_SHOWTEXT, {0}, 0L, 0},
        {-1, ID_MENUE, TBSTATE_ENABLED, TBSTYLE_DROPDOWN|BTNS_SHOWTEXT, {0}, 0L, 0},
        {-1, ID_MENUT, TBSTATE_ENABLED, TBSTYLE_DROPDOWN|BTNS_SHOWTEXT, {0}, 0L, 0},
        {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, {0}, 0L, 0},
        {STD_FILENEW, IDM_NEW, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0L, 0},
        {STD_FILEOPEN, IDM_OPEN, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0L, 0},
    };
    TBADDBITMAP tbBitmap;

    HWND hToolbar = CreateWindowEx(0, TEXT("ToolbarWindow32"), NULL,
        WS_CHILD | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT | TBSTYLE_LIST
        , 0, 0, 0, 0, hWnd,
        (HMENU)wID, GetModuleHandle(NULL), NULL);
    SendMessage(hToolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
    tbBitmap.hInst = HINST_COMMCTRL;
    tbBitmap.nID = IDB_STD_SMALL_COLOR;
    SendMessage(hToolbar, TB_ADDBITMAP, 0, (WPARAM)&tbBitmap);
    for (int i = 0; i < 3; i++) {
        TCHAR szBuf[50];
        GetMenuString(g_hMenu, i, szBuf, 50, MF_BYPOSITION);
        tbb[i].iString = (INT_PTR)SendMessage(hToolbar, TB_ADDSTRING, (WPARAM)0, (LPARAM)szBuf);
    }
    SendMessage(hToolbar, TB_ADDBUTTONS, NELEMS(tbb), (LPARAM)&tbb);
    SendMessage(hToolbar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_MIXEDBUTTONS);
    ShowWindow(hToolbar, SW_SHOW);

    return hToolbar;
}

static HWND MakeStatusbar(HWND hWnd, UINT wID)
{
    HWND hStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL,
        WS_CHILD | WS_VISIBLE,
        0, 0, 0, 0,
        hWnd, (HMENU)wID, GetModuleHandle(NULL), NULL);
    return hStatus;
}

static LRESULT CALLBACK WndProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
    switch (wMsg)
    {
        case WM_COMMAND:
            return OnCommand((hwnd), (int)(LOWORD(wParam)), (HWND) (lParam), (UINT)HIWORD(wParam)), 0;
        case WM_SIZE:
            return OnSize((hwnd), (UINT) (wParam), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam)), 0;
        case WM_NOTIFY:
            return OnNotify(hwnd,(int)(wParam),(NMHDR*)(lParam)), 0;
        case WM_MOUSEMOVE:
            return OnMouseMove(hwnd,(int)(short)LOWORD(lParam),(int)(short)HIWORD(lParam),(UINT)wParam),0;
        case WM_ENTERIDLE:
            return OnEnterIdle(hwnd,(UINT)wParam,(HWND)lParam),0;
        case WM_SYSKEYUP:
            return OnSysKey(hwnd,(UINT)(wParam),FALSE,(int)(short)LOWORD(lParam),(UINT)HIWORD(lParam)),0;
        case WM_CREATE:
            return OnCreate((hwnd), (LPCREATESTRUCT) (lParam)), 0;
        case WM_DESTROY:
            return OnDestroy(hwnd), 0;
        default:
            return DefWindowProc(hwnd, wMsg, wParam, lParam);
    }
}

static BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
    InitCommonControls();
    g_hMenu = LoadMenu(GetModuleHandle(NULL), MAKEINTRESOURCE(2001));
    g_hToolbar = MakeToolbar(hwnd, IDC_TOOLBAR);
    g_hStatus = MakeStatusbar(hwnd, IDC_STATUS);
    return 0;
}

static void OnDestroy(HWND hwnd)
{
    PostQuitMessage(0);
}

static void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
    switch (id)
    {
        case IDM_EXIT:
            PostMessage(hwnd, WM_CLOSE, 0, 0L);
            return;
    }
}

static void OnSize(HWND hwnd, UINT state, int cx, int cy)
{
    if (state == SIZE_MINIMIZED)
        return;
    SendMessage(g_hToolbar, WM_SIZE, state, cx);
    SendMessage(g_hStatus, WM_SIZE, state, cx);
}

static void OnNotify(HWND hwnd, int idCtrl, NMHDR *pNMHdr)
{
    switch (pNMHdr->code)
    {
        case TTN_NEEDTEXT:
        {
            if (((LPTOOLTIPTEXT)pNMHdr)->hdr.idFrom < 7000) {
                ((LPTOOLTIPTEXT)pNMHdr)->hinst = g_hInst;
                ((LPTOOLTIPTEXT)pNMHdr)->lpszText = MAKEINTRESOURCE(((LPTOOLTIPTEXT)pNMHdr)->hdr.idFrom);
            }
            return;
        }
        case TBN_HOTITEMCHANGE:
            if (((LPNMTBHOTITEM)pNMHdr)->idNew >= 7000)
                TBContextMenu(hwnd, (LPNMTOOLBAR)pNMHdr, ((LPNMTBHOTITEM)pNMHdr)->idNew - 7000);
            return;
        case  TBN_DROPDOWN:
        {
            TBContextMenu(hwnd, (LPNMTOOLBAR)pNMHdr, ((LPNMTOOLBAR)pNMHdr)->iItem - 7000);
            return;
        }
    }
}

static void OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
{
    TCHAR szTmp[100];
    wsprintf(szTmp, TEXT("%p x=%d,y=%d"), hwnd, x, y);
    SetWindowText(g_hStatus, szTmp);
}

static void OnEnterIdle(HWND hwnd, UINT source, HWND hwndSource)
{
    if (source == MSGF_MENU) {
        POINT pt;
        RECT rc;
        RECT rcTB, rcTBB;
        int iItem;
        GetCursorPos(&pt);    // mouse pos
        GetWindowRect(hwndSource, &rc);    // menu rect
        GetWindowRect(g_hToolbar, &rcTB);    // toolbar rect
        rcTB.left = rc.left;
        rcTB.right = rc.left;
        iItem = 0;
        while(1) {
            if(!SendMessage(g_hToolbar, TB_GETITEMRECT, iItem, (LPARAM)&rcTBB)) break;
            rcTB.right = rcTB.left + rcTBB.right - rcTBB.left;    // add menu button width
            if (pt.x < rcTB.right) break;
            iItem++;    // next menu button
        }
        //rcTB.bottom += 5;    // avoid gap
        SHORT sKBs = (GetKeyState(VK_LEFT) + GetKeyState(VK_RIGHT)) & 0xFF00;
        if (sKBs || !(PtInRect(&rc, pt) || PtInRect(&rcTB, pt))) {
            SendMessage(hwndSource, WM_KEYDOWN, VK_ESCAPE, 0);
            SendMessage(hwndSource, WM_KEYUP, VK_ESCAPE, 0);
        }
    }
}

static void OnSysKey(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags)
{
    TBContextMenu(hwnd, NULL, 0);
}

static void TBContextMenu(HWND hwnd, NMTOOLBAR *pNMTB, int iItem)
{
    RECT rcTB;
    HMENU hMenu;

    SendMessage(g_hToolbar, TB_GETITEMRECT, iItem, (LPARAM)&rcTB);
    MapWindowPoints(hwnd, HWND_DESKTOP, (LPPOINT)&rcTB, 2);
    if (!pNMTB) SetCursorPos(rcTB.left + 5 , rcTB.bottom + 5);
    hMenu = GetSubMenu(g_hMenu, iItem);
    if (hMenu)
        TrackPopupMenu(hMenu, TPM_LEFTALIGN, rcTB.left , rcTB.bottom, 0, g_hFrame, NULL);
    return;
}
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: NoCforMe on November 06, 2023, 12:56:39 PM
Interesting.

Question: is this the part that creates the menu within the toolbar?
    for (int i = 0; i < 3; i++) {
        TCHAR szBuf[50];
        GetMenuString(g_hMenu, i, szBuf, 50, MF_BYPOSITION);
        tbb[i].iString = (INT_PTR)SendMessage(hToolbar, TB_ADDSTRING, (WPARAM)0, (LPARAM)szBuf);

I don't understand how that works. Does TB_ADDSTRING create the menu items? According to the documentation (https://learn.microsoft.com/en-us/windows/win32/controls/tb-addstring), that message "adds a new string to the toolbar's string pool". I don't see how that leads to a menu being created.
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: TimoVJL on November 06, 2023, 01:01:17 PM
That only copy menu strings to buttons.
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: jj2007 on November 06, 2023, 01:02:48 PM
Quote from: TimoVJL on November 05, 2023, 09:46:59 PMMy bad, wrong example code.
EDIT: TBMenu code

Thanks a lot, Timo :thup:
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: NoCforMe on November 07, 2023, 09:19:47 AM
Back on topic.

Here's the toolstrip module source for your programming pleasure. There's an .asm file, an .inc file and a text file explaining how to use it. (The instructions may look complicated--they're fairly long--but it really isn't hard to use this at all. Easier, I think, than Win32 toolbars.)

This has been minimally tested. No crashes here. Still slight problems w/tooltips (they don't always disappear like they're supposed to). If this is a problem, don't use them (it works fine without tooltips).
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: fearless on November 08, 2023, 01:55:13 AM
I couldnt download the test binary, windows defender kept removing it. However I had a glance at the code.

I would recommend a couple of things: handle the WM_ERASEBACKGROUND message and return TRUE or 1, this informs windows that you will handle the erasing of the background yourself in the WM_PAINT message. Without it, windows will handle the background painting and that might show as flickering as it paints it. This applies to all the controls, but most likely might help fix the flickering issue with the tooltip window.

    .ELSEIF eax == WM_ERASEBKGND
        mov eax, 1
        ret

Add WS_CLIPSIBLINGS style to the tooltip control, again this might help reduce flickering as it instructs windows to not draw the stuff behind the control. Without out, some painting of underlying windows can show as flickering.

I would remove the shadow window for the tooltip as this also might be contributing to the flickering, but with WS_CLIPSIBLINGS style set on the tooltip, it might help.

Ideally i would add a class style when registering the tooltip class by setting the WNDCLASSEX.style flag to CS_DROPSHADOW - this will give the tooltip window the drop shadow that windows uses for dialogs/controls etc. So something like the following as an example:

TooltipRegister PROC
    LOCAL wc:WNDCLASSEX
    LOCAL hinstance:DWORD
   
    Invoke GetModuleHandle, NULL
    mov hinstance, eax

    invoke GetClassInfoEx, hinstance, Addr szMyTooltipClass, Addr wc
    .IF eax == 0 ; if class not already registered do so
        mov wc.cbSize, SIZEOF WNDCLASSEX
        lea eax,  szMyTooltipClass
        mov wc.lpszClassName, eax
        mov eax, hinstance
        mov wc.hInstance, eax
        lea eax, MyTooltipWndProc
        mov wc.lpfnWndProc, eax
        Invoke LoadCursor, NULL, IDC_ARROW
        mov wc.hCursor, eax
        mov wc.hIcon, 0
        mov wc.hIconSm, 0
        mov wc.lpszMenuName, NULL
        mov wc.hbrBackground, NULL
        mov wc.style, CS_DROPSHADOW ; add drop shadow style
        mov wc.cbClsExtra, 0
        mov wc.cbWndExtra, 0
        Invoke RegisterClassEx, addr wc
    .ENDIF 
    ret
TooltipRegister ENDP

Also when you do this, there is a bug or glitch that sometimes prevents the drop shadow from showing the first time, but will show the next time onwards, so to fix that, maybe in WM_NCCREATE or WM_CREATE of the tooltip window proc add the following:

; Fix for drop shadow not showing first time, only on second time onwards
Invoke SetWindowPos, hWin, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE
Invoke SetWindowPos, hWin, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE

For the tooltip window, I would create it early on and then show it when needed, and hide it when not needed instead of creating and destroying it

Anyhow hope that helps
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: NoCforMe on November 08, 2023, 06:42:54 AM
All good stuff. Thanks!

I like your suggestion of CS_DROPSHADOW: let the OS do it (it'll do a nicer job anyhow). So I tried it: couldn't get it to work.

My class styles were already CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW or CS_DBLCLKS, so I tried ORing in CS_DROPSHADOW. Didn't work. Tried just setting that one style:
        MOV WC.style, CS_DROPSHADOW
That didn't work either.

You didn't mention it, but you need to enable drop shadows through SystemParametersInfo(). I did that; still no joy:
INVOKE SystemParametersInfo, SPI_SETDROPSHADOW, 0, TRUE, 0

A subsequent SPI_GETDROPSHADOW inquiry confirms that this is set (to TRUE).

I assume this only needs to be done once, right?, as it changes a global setting. Tried setting this before registering the class, after, both, before creating the window, after, both: still didn't work.

Oh, and I'm definitely creating the tooltip window as a popup, not a child (drop shadows don't work on child windows):
$tooltipWinStyles EQU WS_POPUP or WS_BORDER or WS_VISIBLE

Any idea what I'm doing wrong here?

Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: fearless on November 09, 2023, 05:06:21 AM
Hi,

There is actually a few gotchas in relation to creating the drop shadow. I took a deep dive myself last night and today to have a look into it, referring back to some of my other projects etc to see what i had done.

    .ELSEIF eax == WM_CREATE
        ; Remove WS_POPUP style and replace with WS_CHILD
        ; this prevents losing focus from main dialog to the tooltip
        Invoke GetWindowLong, hWin, GWL_STYLE
        or eax, WS_CHILD or WS_CLIPSIBLINGS
        and eax, (-1 xor WS_POPUP)
        Invoke SetWindowLong, hWin, GWL_STYLE, eax
       
        ; Required when we change style to use this
        Invoke SetWindowPos, hWin, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE or SWP_NOSENDCHANGING or SWP_NOSIZE or SWP_NOMOVE or SWP_DRAWFRAME

I looked at the issue with the tooltip still being visible sometimes if the mouse is moved fast enough away from the toolstrip. To handle this I placed a call to TrackMouseEvent inside the mousemove event with a flag to prevent it being called more than once:

do_mousemove:
    .IF TSMouseOverTracking == FALSE
    MOV tme.cbSize, SIZEOF TRACKMOUSEEVENT
    MOV tme.dwFlags, TME_LEAVE
    MOV EAX, hWin
    MOV tme.hwndTrack, EAX
    INVOKE TrackMouseEvent, ADDR tme
    mov TSMouseOverTracking, TRUE
    .ENDIF

With this in place, the toolstrip will now receive the WM_MOUSELEAVE message and and during that we can hide the tooltip.

I also added a new paint proc for the tooltip to double buffer the painting to eliminate any flickering from the tooltip - the same could be done for the toolstrip and inclusion of WS_CLIPCHILDREN in the toolstrip should also help prevent any flickering from it.

Attached is the modified Toolstrip.asm file with my changes/additions.

Also you can create a .lib library from your source instead of just the .obj file. I added the settings I used at the top of the Toolstrip.asm file

Hope that helps
Title: Re: MyToolstrip: A possible replacement for the Windows toolbar
Post by: NoCforMe on November 09, 2023, 12:27:04 PM
Wow. I just saw this. You put a lot of work into that! You'll have to give me a little time to look at this, but I definitely will. Thanks!