Step-by-Step imitation QQ interface (6): customized menu and built-in scroll bar

Source: Internet
Author: User
Tags image flip

This article demonstrates how to customize the menu style and scroll bar on the imitation QQ interface.


First, I will explain how to implement a menu with rounded corners of QQ. This requires hook, because the menu is a special window and cannot be obtained using findwindow or hmenu, it cannot be subclass. A hook is required. Here the wh_callwndproc hook is used:


C/C ++ code
   Bool qqmenu: installhook ()
If (m_hmenuhook = NULL)
M_hmenuhook = setwindowshookex (wh_callwndproc, menuhook, getinstance (), getcurrentthreadid ());
Return (bool) m_hmenuhook;


Lresult callback qqmenu: menuhook (

Int code, wparam, lparam)
Pcwpstruct lpcwp = (pcwpstruct) lparam;

If (code = hc_action & lpcwp-> message = wm_create)
// Check whether it is a menu
Tchar szclassname [16];
Int ncount = getclassname (lpcwp-> hwnd, szclassname, 12 );
If (ncount = 6 & lstrcmpi (szclassname, _ T ("#32768") = 0)
Qqmenu * pmenu = (qqmenu *) getwindowptr (lpcwp-> hwnd );
If (pmenu = NULL)
// Not subclass
Pmenu = Window-> newringobj (pmenu, true );
Pmenu-> attach (lpcwp-> hwnd );
Return callnexthookex (m_hmenuhook, code, wparam, lparam );



Check msdn to know that the window class name of the menu is "#32768". Find the menu window and you will be able to do it after subclass. There are not many hands and feet to do, you only need to implement the shape of the rounded rectangle. The menu self-painting has already been implemented and is handled by the main window, so you don't have to bother yourself.

The first is the wm_nccalcsize message. You need to increase the number of non-customer zones to free up space for the rounded corners, but note that the rounded corners are made and the area of the customer zone is reduced, the menu items may not be displayed completely. Therefore, you need to spit out the space of the customer partition in the wm_windowposchanging message to increase the size of the menu window so that the display of the menu items is not affected. The first article in the series of articles on how to implement and draw a rounded rectangle window has been mentioned.

A friend posted a post in front of my blog. why can't the wm_mousemove message be intercepted after the subclass menu? I did not try it. In fact, the wm_mousemove message in the menu is replaced, changed to mn_mousemove message, the value is 0x01ee, defined to see this link:

Next I will explain how to implement the special menu displayed on the button in the lower left corner of QQ. The general practice is to implement a special window and load the menu, but since the menu is hooked, it is better to implement it directly on the menu. The first question is how to differentiate the main menu from other menu rounded corners to achieve different processing. The method is to call setmenuinfo when creating the main menu to set a value for it. In the future, you can use getmenuinfo to check this value to identify the main menu:


C/C ++ code
   // Set the data identification QQ Main Menu
Menuinfo MF;
Mf. cbsize = sizeof (menuinfo );
Mf. fmask = mim_menudata;
Mf. dwmenudata = id_qqmenu;
M_rmqq-> setmenuinfo (& MF );


After the main menu is identified, different shapes can be processed. There are two schemes for the shape of the menu. One is to bring the shape of the QQ circle button and cover the QQ button when it pops up. However, considering that the QQ pattern cannot be colored, we need to draw the Penguin with multiple hands and feet during the painting. We still need to skip the shape of the circle, for example (the image on the left ):

Because menu items can be increased or decreased, this shape must be dynamically generated. If you have a good ry, you can draw it out with lines. I am not doing well in geometry, and I forgot it: P, therefore, this shape is constructed using rounded rectangle and image synthesis. In the right figure, the blue border outlines the rounded rectangle, which can be generated using createroundrectrgn. The red dotted line shows the part that needs to be dug and added in the rounded rectangle area, the yellow color is the overlapping part of the two hrgn, and the area is fixed and will not change. Therefore, you can use the image to generate the hrgn based on this image. This image is as follows:

The shape is a bit weird. The black part is the hrgn shape to be generated. After hrgn is generated, you can call combinergn to merge it with the hrgn of the rounded rectangle. The parameter uses rgn_xor:


C/C ++ code

M_hrgn is the rounded rectangle, and hrgn is the hrgn of the black part in the figure. The rgn_xor parameter is used to merge two shapes to remove overlapping parts, the result is that the rounded rectangle is located in the lower left corner of the image, and the arc triangle is added, which exactly matches the shape of the QQ main menu.


The following explains how to generate hrgn Based on images. The principle is to scan pixels row by row. If it is white (transparent), it is not processed. If it is black (non-transparent) createrectrgn is called based on the number of consecutive pixels to generate an hrgn with a height of 1. Each scan line may have some discontinuous points or lines. You can use combinergn, the rgn_or parameter is used to combine these discontinuous hrgn, and then combine the hrgn generated by all the scan rows of the entire image to obtain the hrgn with the specified shape in the image. The code is a bit complicated, I will not post it here. You can go to the image library libsrc \ ringdib \ rdib of ringsdk. the ringdib: creatergn function code in CPP.

After obtaining the final hrgn, you can use setjavaswrgn to implement the special-shaped menu. Of course, you also need to prepare the texture of the non-customer area of the window. The texture in the lower left corner is as follows:

I will not talk about how to draw textures. I have explained the previous articles.

Next is the menu pop-up problem. After calculating the position pop-up, it seems OK. In fact, there is still work to be done. Move the window to the top of the desktop, then click the button to bring up the menu, dizzy, the menu is popped up, and the gap goes down to the lower part of the main interface. The menu pop-up direction must be restricted. In this case, the last parameter lptpmparams of the trackpopupmenuex function is used, this parameter is usually set to null. In fact, this parameter can be used to limit the menu pop-up direction. This parameter is described as follows:


C/C ++ code
   typedef struct tagTPMPARAMS { 
UINT cbSize;
RECT rcExclude;


cbSize :Size of structure,

in bytes.
rcExclude :Rectangle to exclude when positioning the window, in screen coordinates

Specify the coordinates of rcexclude. When the menu pops up, it will avoid this area. We will assign the space coordinates at the bottom of the main window to this rcexclude. When the main menu pops up, we can only pop up the page to avoid overwriting the coordinates, this has finally achieved the expected results. However, the rcexclude is just a rectangle. We stopped the menu and couldn't play down, but left or right. We moved the window to the rightmost of the screen, then the menu popped up and continued to dizzy, the menu is displayed to the left! The gap ran to the left and exposed the stuffing. There is no clever way to do this. You can only flip the left and right sides of the menu shape. You also need to adjust the pop-up position of the menu to set the gap to the QQ circle button. The pop-up menu code is as follows:



C/C ++ code
   Ringcmd (wndqqbutton, onqqmenupopup)
If (event = stn_clicked)
Rect RC, rcex;
Int xoff = 0;
Getwindowrect (m_hwnd, & rc );
RC. Left + = qqbtn_x;
RC. Bottom-= (window-> m_dibqqcorner.height () + qqbtn_qq_height + 3 );
// If the menu is displayed on the right side of the screen and you need to flip its irregular shape horizontally, you need to adjust the X coordinates of the pop-up position.
If (RC. Left + qqmenu_width> winvar (sm_cxscreen ))
Xoff = qqmenu_offset;
// Restrict the menu pop-up position so that it always pops up.
Rcex. Left = 0;
Rcex. Right = RC. Right;
Rcex. Top = RC. Bottom + 1;
Rcex. Bottom = winvar (sm_cyscreen );
// Set the flag to refresh the notification and draw the penguin on the QQ button
M_bmenupopup = true;
Invalidaterect (m_hwnd, null, true );
// In the pop-up menu, set to avoid the coordinates specified by rcex
Window-> m_rmqq-> popupex (m_hwnd, RC. Left + xoff, RC. Bottom, false, & rcex );


The flip menu has an irregular shape. In fact, the hrgn image in the lower left corner is first flipped horizontally and then hrgn is generated. Horizontal image flip is actually to reverse the data array of each row of the image. It is a C language entry-level course.

Now, the QQ special-shaped main menu is finally implemented, and the scroll bar in the last friend list area is left.

Implementation of the Self-painted scroll bar:

The self-painted scroll bar is actually very simple. You should have read the articles earlier in this series. It is not very difficult to map after subclass. However, the self-painting of the QQ friends list area is not that simple, because the friends list is implemented using the ListBox control, while the ListBox scroll bar is built-in, drawn, not a window at all! There is no way to subclass it. Now it is finished. You can only subclass ListBox and draw the code of the scroll bar by yourself. However, The ListBox painting scroll bar does not provide an interface or a message. It is useless to draw a scroll bar in wm_ncpaint. Many messages are directly drawn by the scroll bar, in this case, we need to intercept all the messages that may draw the scroll bar and process them by ourselves. In addition to wm_ncpaint, the message to be intercepted is as follows:


That dizzy! It is better to write a window for implementation. The fact is that it is enough time to study these messages to write a window. However, it makes sense to implement the auto-painting of The ListBox scroll bar. Let's talk about messages one by one.

First of all, it is not difficult to implement the wm_ncpaint message and draw the scroll bar. After implementation, the window is overwritten by other programs, and then switched to the front-end. the scroll bar looks like it is, but the scroll bar shows up when it moves.

Then wm_vscroll. The ListBox window type is lbs_disablenoscroll, indicating that the window can still be rolled without a scroll bar. Therefore, in this message, remove the ws_vscroll type of the window first, then, call the default window process and set the ws_vscroll type back. Now the effect is that rolling by page will not show the filling, and the position of the scroll bar will not be set when the wm_vscroll message is sent.


C/C ++ code
   case WM_VSCROLL:
LRESULT res = RingListBox::RingdowProc(m_hWnd,param);
return res;


The wm_size message is processed in the same way, so the window size adjustment will not be explicit.

Wm_stylechanged, wm_lbuttondblclk, and wm_nclbuttondblclk intercept the messages and return 0 directly. Double-click the messages in the window to avoid any problem.

Wm_lbuttondown, because the lbs_nointegralheight type of ListBox is set, the bottom visual option of ListBox may be incomplete. If this option is selected, the ListBox will scroll through the window to display this option completely, and the scroll bar will be re-painted. Therefore, we need to check in this message if the selected option is not displayed completely, send the wm_vscroll message whose wparam is sb_linedown To Make It display completely. Then, select this option and return 0. In other cases, submit it to the default Window Process for processing.

Wm_mousewheel: Calculate the rolling position, and then send the following two messages in a row.


C/C ++ code

Next we will go to the most difficult part. Click the up and down arrow on the scroll bar and drag the slider. This requires that the wm_nclbuttondown and wm_lbuttonup messages be processed first. In the wm_nclbuttondown message, the system first checks whether the mouse is on the scroll bar. In this case, setcapture captures the mouse. If the cursor is on the up or down arrow, set the timer to send the wm_vscroll messages in the wm_timer message that are sb_lineup, sb_linedown, sb_pageup, and sb_pagedown, which can be continuously rolled. In the wm_lbuttonup message, releasecapture is cleared, the timer is cleared, and the wm_vscroll message where wparam is sb_endscroll is sent to terminate the rolling.


The most difficult part is the processing of the slider. You must draw the slider position by yourself. The timer detects and draws the slider position. When the slider movement distance is detected, You Need To scroll the window, calculate the slider position of the scroll bar and send the wm_vscroll message whose wparam is sb_thumbposition. The scroll range (value returned by getscrollrange) of The ListBox is the total number of options-1, and the scroll range is pixel-based, therefore, it is inevitable that there will be errors during calculation, which may cause occasional jitter when the slider is dragged. However, the built-in scroll bar self-painting of ListBox is realized. This is the first time that the demo program does this, code optimization and adjustment are required to reach the commercial level.

Now let's look at the program's:

Finally, let's take a sigh of relief. Thank you for your support and encouragement.


Note: several bugs of ringsdk are found and corrected when this demo program is compiled. Therefore, the latest version of ringsdk must be updated to compile this demo program, if this parameter is not updated, the main menu and some color matching functions may be abnormal, but the effect demonstration will not be affected. The source code of this demo program has been submitted to SVN. It will be automatically downloaded when the ringsdk is updated. You do not need to go to the demo program.

This article from the csdn blog, reproduced please indicate the source:

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.