Implementation of the drag and drop function of controls in duilib (with source code), duilib drag and drop
Reprinted, please describe the original source. Thank you ~~ : Http://blog.csdn.net/zhuhongshu/article/details/41144283
In the duilib library, the drag and drop function is added to the control, which is not displayed. In actual use, the drag function is also useful. See how duilib supports drag and drop in the group. I will also write this article to describe how duilib Implements control drag and drop.
When I first came into contact with duilib, I thought about the duilib drag-and-drop feature. The idea was that I set a floating control in the xml layout, which is hidden normally, when the drag condition is set, it is displayed and moved with the mouse. This method is obviously not elegant. After coding, the coupling is too high due to other controls, and it is subject to many restrictions. It is not easy to encapsulate the drag-and-drop function into a single control.
After reading the duilib source code and gradually getting familiar with him, I found that although duilib does not display the supported drag controls, it has already had the necessary drag conditions and mechanisms, and has been used in some controls! For example, the sepwidth attribute and sepimm attribute of the horizontal and vertical layout can be used to manually drag the border of the layout to change the size, and the ListHeader can be dragged to change its own width.
Analysis mechanism:
Here I will analyze the drag-and-drop mechanism of the horizontal layout CHorizontalLayoutUI. The two key attributes are described as follows:
<Attribute name = "sepwidth" default = "0" type = "INT" comment = "separator width. Positive and Negative separators are left or right, for example, (-4) "/> <Attribute name =" sepimm "default =" false "type =" BOOL "comment =" whether to drag the separator to change the size immediately, such as (false) "/>
The sepwidth attribute is very simple. The sepimm attribute indicates whether to change the size immediately when dragging the layout separator. The following two images do not change the size immediately or immediately:
You can see that the drag without changing the size immediately has a translucent black shadow to indicate the current drag position. The drag immediately changes the container size. The implementation code of the drag-and-drop function is mainly completed in the DoEvent function and DoPostPaint function. The DoEvent Function Code is as follows:
void CHorizontalLayoutUI::DoEvent(TEventUI& event){if( m_iSepWidth != 0 ) {if( event.Type == UIEVENT_BUTTONDOWN && IsEnabled() ){RECT rcSeparator = GetThumbRect(false);if( ::PtInRect(&rcSeparator, event.ptMouse) ) {m_uButtonState |= UISTATE_CAPTURED;ptLastMouse = event.ptMouse;m_rcNewPos = m_rcItem;if( !m_bImmMode && m_pManager ) m_pManager->AddPostPaint(this);return;}}if( event.Type == UIEVENT_BUTTONUP ){if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) {m_uButtonState &= ~UISTATE_CAPTURED;m_rcItem = m_rcNewPos;if( !m_bImmMode && m_pManager ) m_pManager->RemovePostPaint(this);NeedParentUpdate();return;}}if( event.Type == UIEVENT_MOUSEMOVE ){if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) {LONG cx = event.ptMouse.x - ptLastMouse.x;ptLastMouse = event.ptMouse;RECT rc = m_rcNewPos;if( m_iSepWidth >= 0 ) {if( cx > 0 && event.ptMouse.x < m_rcNewPos.right - m_iSepWidth ) return;if( cx < 0 && event.ptMouse.x > m_rcNewPos.right ) return;rc.right += cx;if( rc.right - rc.left <= GetMinWidth() ) {if( m_rcNewPos.right - m_rcNewPos.left <= GetMinWidth() ) return;rc.right = rc.left + GetMinWidth();}if( rc.right - rc.left >= GetMaxWidth() ) {if( m_rcNewPos.right - m_rcNewPos.left >= GetMaxWidth() ) return;rc.right = rc.left + GetMaxWidth();}}else {if( cx > 0 && event.ptMouse.x < m_rcNewPos.left ) return;if( cx < 0 && event.ptMouse.x > m_rcNewPos.left - m_iSepWidth ) return;rc.left += cx;if( rc.right - rc.left <= GetMinWidth() ) {if( m_rcNewPos.right - m_rcNewPos.left <= GetMinWidth() ) return;rc.left = rc.right - GetMinWidth();}if( rc.right - rc.left >= GetMaxWidth() ) {if( m_rcNewPos.right - m_rcNewPos.left >= GetMaxWidth() ) return;rc.left = rc.right - GetMaxWidth();}}CDuiRect rcInvalidate = GetThumbRect(true);m_rcNewPos = rc;m_cxyFixed.cx = m_rcNewPos.right - m_rcNewPos.left;if( m_bImmMode ) {m_rcItem = m_rcNewPos;NeedParentUpdate();}else {rcInvalidate.Join(GetThumbRect(true));rcInvalidate.Join(GetThumbRect(false));if( m_pManager ) m_pManager->Invalidate(rcInvalidate);}return;}}if( event.Type == UIEVENT_SETCURSOR ){RECT rcSeparator = GetThumbRect(false);if( IsEnabled() && ::PtInRect(&rcSeparator, event.ptMouse) ) {::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_SIZEWE)));return;}}}CContainerUI::DoEvent(event);}
In the DoEvent function, the function is completed by using three events: UIEVENT_BUTTONDOWN, UIEVENT_BUTTONUP, and UIEVENT_MOUSEMOVE.
1. In the UIEVENT_BUTTONDOWN event, assign the m_uButtonState variable to UISTATE_CAPTURED, which indicates whether the container is currently in the drag-and-drop status. The ptLastMouse variable is assigned the coordinates of the current mouse; m_rcNewPos is assigned the position of the current container. It can be seen from the name that it represents the new Location Area of the control. Then, depending on the status of the m_bImmMode variable (indicating whether to change the size immediately), determine whether to call the AddPostPaint function (this function is critical and will be discussed later)
2. In the UIEVENT_MOUSEMOVE event, LONG cx = event. ptMouse. x-ptLastMouse. x; calculates the offset of the mouse movement process, which is used to calculate the code at the end of the event processing.
if( m_bImmMode ) {m_rcItem = m_rcNewPos;NeedParentUpdate();}else {rcInvalidate.Join(GetThumbRect(true));rcInvalidate.Join(GetThumbRect(false));if( m_pManager ) m_pManager->Invalidate(rcInvalidate);}
Select two plotting methods based on m_bImmMode. One is to directly specify the container as the new area size, the other is to specify an invalid area, and then the DoPostPaint function is used to complete the painting.
3. In the UIEVENT_BUTTONUP event, the m_uButtonState variable is canceled to the UISTATE_CAPTURED state and the interface is forcibly refreshed.
Analysis completed:
In the content mentioned above, it is easy to understand how to change the container size immediately. Instead of changing the size and position immediately, I will discuss how to implement the shadow effect. This effect is more commonly used. For example, when we drag a friend to another group in the IM software, there is often a shadow box representing a friend moving with the mouse, which can be implemented through the code here.
To change the size and position without immediate modification, you must use your own control to override the DoPostPaint function, and register yourself by calling the AddPostPaint function of PaintManager, for example, the above example calls AddPostPaint in the UIEVENT_BUTTONDOWN event and RemovePostPaint in UIEVENT_BUTTONUP.
After AddPostPaint is called, PaintManager will draw a complete interface in the WM_PAINT message and call the DoPostPaint function of our control to complete the painting by ourselves! In this case, we can complete the shadow painting in the DoPostPaint function. If we do not use this DuiLib mechanism to draw shadows directly in the DoPaint function, we will not be able to achieve what we need, because the software interface may not be fully drawn when the DoPaint function is called, the shadow we draw may be overwritten by other controlled DoPaint functions!
It should be noted that in the DoPosiPaint function, the draw range is not only the scope of the control, but the customer area of the entire program. Therefore, the drag-and-drop shadow can be painted to any position of the form, of course, we can also control the drawn range through code judgment. This is what I mentioned earlier, duilib itself already has a drag-and-drop mechanism and conditions.
Take a look at the DoPostPaint function code:
void CHorizontalLayoutUI::DoPostPaint(HDC hDC, const RECT& rcPaint){if( (m_uButtonState & UISTATE_CAPTURED) != 0 && !m_bImmMode ) {RECT rcSeparator = GetThumbRect(true);CRenderEngine::DrawColor(hDC, rcSeparator, 0xAA000000);}}
The function uses the GetThumbRect function to obtain the position of the current Shard, and then draws a translucent black shadow. The following describes how to implement the drag and drop function of controls in duilib.
Drag by yourself:
We can also have two drag-and-drop solutions:
1. drag-and-drop mode:
Inherit the required control and override the DoEvent function. In the UIEVENT_BUTTONDOWN, UIEVENT_BUTTONUP, and UIEVENT_MOUSEMOVE events, you can directly change the coordinates and size of the Control Based on the mouse position, obviously, this requires us to set the float attribute to true. This method is easy to implement and I will not write code.
2. Non-instant drag mode:
1. inherit the required control and override the DoEvent function. In the three events of UIEVENT_BUTTONDOWN, UIEVENT_BUTTONUP, and UIEVENT_MOUSEMOVE, rewrite the DoPostPaint function to draw the shadow.
2. Call the AddPostPaint function in the UIEVENT_BUTTONDOWN event to register the control.
3. Calculate the new control location in the UIEVENT_MOUSEMOVE event, and combine the New and Old locations to call the Invalidate function to refresh the location (otherwise there will be a shadow)
4. Call RemovePostPaint in the UIEVENT_BUTTONUP event to register yourself and refresh the control.
I wrote a simple example and the experiment succeeded. Here is the experiment code:
Void CModulePaneCellUI: DoEvent (TEventUI & event) {if (event. type = UIEVENT_BUTTONDOWN & IsEnabled () {if (: PtInRect (& m_rcItem, event. ptMouse) {m_uButtonState | = UISTATE_CAPTURED; m_ptLastMouse = event. ptMouse; m_rcNewPos = m_rcItem; if (m_pManager) m_pManager-> AddPostPaint (this); return ;}} if (event. type = UIEVENT_BUTTONUP) {if (m_uButtonState & UISTATE_CAPTURED )! = 0) {m_uButtonState & = ~ Optional; CModulePaneConfigUI * pParent = static_cast <CModulePaneConfigUI *> (m_pParent); pParent-> policydrag (this); if (m_pManager) {m_pManager-> RemovePostPaint (this ); m_pManager-> Invalidate (m_rcNewPos);} NeedParentUpdate (); return ;}} if (event. type = UIEVENT_MOUSEMOVE) {if (m_uButtonState & UISTATE_CAPTURED )! = 0) {LONG cx = event. ptMouse. x-m_ptLastMouse.x; LONG cy = event. ptMouse. y-m_ptLastMouse.y; m_ptLastMouse = event. ptMouse; RECT rcCurPos = m_rcNewPos; rcCurPos. left + = cx; rcCurPos. right + = cx; rcCurPos. top + = cy; rcCurPos. bottom + = cy; // refresh CDuiRect rcInvalidate = m_rcNewPos; m_rcNewPos = rcCurPos; rcInvalidate. join (m_rcNewPos); if (m_pManager) m_pManager-> Invalidate (rcInvalida Te); return ;}} if (event. type = UIEVENT_SETCURSOR) {if (IsEnabled () {: SetCursor (: LoadCursor (NULL, MAKEINTRESOURCE (IDC_HAND); return ;}} CLabelUI :: doEvent (event);} void CModulePaneCellUI: DoPostPaint (HDC hDC, const RECT & rcPaint) {if (m_uButtonState & UISTATE_CAPTURED )! = 0) {CDuiRect rcParent = m_pParent-> GetPos (); RECT rcUpdate = {0}; rcUpdate. left = m_rcNewPos.left <rcParent. left? RcParent. left: m_rcNewPos.left; rcUpdate. top = m_rcNewPos.top <rcParent. top? RcParent. top: m_rcNewPos.top; rcUpdate. right = m_rcNewPos.right> rcParent. right? RcParent. right: m_rcNewPos.right; rcUpdate. bottom = m_rcNewPos.bottom> rcParent. bottom? RcParent. bottom: m_rcNewPos.bottom; CRenderEngine: DrawColor (hDC, rcUpdate, 0xAA000000 );}}
This is the experiment effect. The blue part is the original button control, and the black shadow is being dragged. In DoPostPaint, I control that the shadow painting range cannot exceed the parent container. This is just an experiment. I didn't use good-looking materials to do it, but it proved feasible. After replacing the materials, it will become very nice:
With this method, you can modify the original duilib code. The List control and TreeView control support the drag-and-drop function, and you can also customize some special-purpose controls. The following describes how to draw drag effects. The specific usage is variable!
Summary:
This is the code I wrote to complete a function. I hope it can help my friends who need it. If there is a bug in the Code, or there is a better drag implementation method, please contact me or leave a message!
Redrain 2014.11.15
QQ: 491646717