Xlib tutorial part 12 -- Better Menus

by Alan at Mon 30th Mar 2009 1:00AM EST

Hello, and welcome to part 12 of this Xlib tutorial.

After lasat week, I thought we should take it a little easier. I might have some space this time to explain a few things I didn't last week.

First off, get the code here.

You may notice that the menus line up nicely this time. Most of that is due to a few lines in button.c:


 static void menuBarButtonPress(Block *block, XEvent *ev){
        MenuBarButton *mbb = &block->menubarbutton;
        int x, y;
        x = ev->xbutton.x_root - ev->xbutton.x;
        y = ev->xbutton.y_root - ev->xbutton.y + 20;
        XMoveWindow(ev->xbutton.display, mbb->menu, x, y);
        XMapWindow(ev->xbutton.display, mbb->menu);
}
static void menuBarButtonRelease(Block *block, XEvent *ev){
    MenuBarButton *mbb = &block->menubarbutton;
    XUnmapWindow(ev->xbutton.display, mbb->menu);
}

Base on the name, you should be able to guess that I hooked it up so that it will be called when our menu bar buttons are pressed. There's a type (MenuBarButton) that is new from last week. It remembers where its button window actually is (thanks to the menubar calling resizeBlock()) and causes the menu to show up relative to it when the button is pressed rather than where the mouse pointer is at the time.


void resizeBlock(Block *block, Window win, int width, int height, XEvent *ev){
    XEvent temp;
    XResizeWindow(ev->xany.display, win, width, height);
    if (!(block &&  block->funcs && block->funcs->configureNotify))
        return;
    temp.xconfigure.type = ev->xany.type;
    temp.xconfigure.display = ev->xany.display;
    temp.xconfigure.window = win;
    temp.xconfigure.event = win;
    temp.xconfigure.width = width;
    temp.xconfigure.height = height;
    temp.xconfigure.border_width = 0; /* assume we never use borders */
    temp.xconfigure.above = None;
    temp.xconfigure.override_redirect = False;
    block->funcs->configureNotify(block, &temp);
}

resizeBlock() will work on any of the window blocks in our programs so far to resize them as the parent desires and to let the block know it's resized so it can do something without waiting for a round trip to the server. (The server will also tell us that the window has been resized, but if we don't wait a long chain of resizes won't take as long...)

I'm actually cheating here, creating a false X configure event and sending it. This should cause any problems, since the window is being resized anyway. We only have to be careful we don't send events that aren't going to happen anyway, since we may get other parts of the program into odd states.

There's only one other important change, and that's to the way menubar buttons are created. Last week we just used a normal button and passed the button release event to the callback. Which was fine except that bit of code didn't know where the menu should pop up. We've stopped callbacks from happening and now instead let the menubarbutton know what window it should pop up.

Since most of the code to create either button is the same, I've reused most of it and only split off parts into newButton() and newMemnuBarButton(), calling the original function with the common parts the unimaginative name of newXButton().

Things to try:

Comments are closed.