Duilib implementation of drag control (source code)

Source: Internet
Author: User

Reprint please indicate the original source. Thanks, ~~:http://blog.csdn.net/zhuhongshu/article/details/41144283.


Add drag-and-drop functionality to controls that were not originally displayed in the Duilib library. and the actual use of the drag-and-drop function is also practical.

See some people in the group Duilib How to support drag and drop. I will write this article to explain the duilib implementation of the control drag method.


When I first approached Duilib, I thought about the duilib drag-and-drop feature, and the idea was to set a floating control in the XML layout. He is hidden under normal conditions. When the drag-and-drop condition is set, it is displayed and the trailing mouse moves.

This method is obviously not elegant, after writing the code, because there are other controls, so the coupling is too high, but also subject to a lot of constraints, not easy to wrap the drag function into a single control.


later read the source code of Duilib and slowly familiar with him, found that actually. Duilib Although there is no display of support controls to drag and drop, he already has the necessary conditions and mechanisms for control dragging, and has been used in some controls! For example, the Sepwidth property and the Sepimm property of the horizontal portrait layout support the layout to manually drag the border to resize, listheader support Drag to change the width of itself.


Analysis mechanism:

Here I first analyze the horizontal layout chorizontallayoutui drag mechanism, the introduction of two key properties such as the following:

<attribute name= "sepwidth" default= "0" type= "INT" comment= "delimiter width, plus or minus indicates delimiter on left or right, e.g. ( -4)"/><attribute name= " Sepimm "default=" false "Type=" BOOL "comment=" Drag the delimiter to change the size immediately, such as (false) "/>

Sepwidth properties Very easy, I will not say. The Sepimm property indicates whether the size is changed immediately when you drag the layout's delimiter. The following two images are not immediately changed and changed in size:

watermark/2/text/ahr0cdovl2jsb2cuy3nkbi5uzxqvemh1ag9uz3nodq==/font/5a6l5l2t/fontsize/400/fill/i0jbqkfcma==/ Dissolve/70/gravity/center ">



Can see the drag that does not change the size immediately. There will be a translucent black shadow to represent the position of the current drag. Dragging immediately changes the size of the container. The implementation code of the drag-and-drop function is mostly completed in the Doevent function and the Dopostpaint function. Doevent function code such as the following:

void Chorizontallayoutui::D oevent (teventui& event) {if (m_isepwidth! = 0) {if (event. Type = = Uievent_buttondown && isenabled ()) {RECT Rcseparator = GetThumbRect (false); if (::P tinrect (& 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_r CItem = 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_isepwid TH) 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 & lt;= 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 () &&::P tinrect (& Rcseparator, Event.ptmouse) {:: SetCursor (:: LoadCursor (NULL, Makeintresource (Idc_sizewe)); return;}}} Ccontainerui::D oevent (event);}


In the Doevent function, the main use of Uievent_buttondown, Uievent_buttonup, uievent_mousemove three events completed this function.

1, in the Uievent_buttondown event, the m_ubuttonstate variable is assigned to uistate_captured, this flag is used to indicate whether the container is currently in a drag state, the Ptlastmouse variable is assigned to the current mouse coordinates; m The _rcnewpos assignment is the location of the current container, which can be seen from the name of the area where he represents the new position of the control. Then decide whether to call the Addpostpaint function based on the state of the M_bimmmode variable (indicating whether to change the size right away) (this function is very critical, later)


2, in the Uievent_mousemove incident. LONG cx = event.ptmouse.x-ptlastmouse.x; Calculates the offset of the mouse movement to calculate the new position of the control movement, 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);}

based onM_bimmmode to choose two ways of drawing. One is to specify the container directly for the new area size. Another is to specify an invalid area and then the Dopostpaint function to complete the drawing work.


3, in the Uievent_buttonup incident. will be the m_ubuttonstate variable is canceled to uistate_captured status. and forced to refresh the interface.


Analysis Completed:


In the above mentioned content, immediately change the size of the container is very easy to understand. I won't explain it anymore. I said not to change the size and position immediately, but to show how a shadow effect is implemented.

This effect is more often used, for example, when we are in the IM software, drag a friend to a group, often there is a shadow box on behalf of friends to follow the mouse movement, can be achieved through the code here.


To implement a non-immediate change in size and position. You must use your own control to override the Dopostpaint function and, if appropriate, call Paintmanager's addpostpaint function to register yourself, such as the example above in Uievent_ The Buttondown event calls Addpostpaint and calls Removepostpaint in Uievent_buttonup.


After calling Addpostpaint, Paintmanager will finish drawing a complete interface in the WM_PAINT message. To call our control's Dopostpaint function, let's finish drawing it ourselves! At this point we can finish drawing the shadow in the Dopostpaint function.

Assume that you do not use this mechanism of duilib to draw shadows directly in the DoPaint function. will not be able to achieve the effect we need, because in the DoPaint function call when the software interface may not be completely painted, we draw the shadow may be covered by other control of the DoPaint function!


It is necessary to note that in the Doposipaint function, the scope of the drawing is not the scope of the control, but the client area of the entire program. So this drag shadow can be drawn anywhere in the window, and of course we can control the scope of the drawing through code inference, which is what I said earlier that Duilib itself already has the mechanism and the condition to support drag and drop.


Take a look at the function code of Dopostpaint:

void Chorizontallayoutui::D opostpaint (hdc hdc, const rect& rcpaint) {if (M_ubuttonstate & uistate_captured)! = 0 &&!m_bimmmode) {RECT Rcseparator = GetThumbRect (true); Crenderengine::D rawcolor (HDC, Rcseparator, 0xaa000000);}

The function obtains the position of the current cutting bar through the GetThumbRect function, and then draws a translucent black shadow. Speaking of this, we have fully explained the implementation of the control drag and drop function in Duilib.


Self-implementation drag and drop:

We can have two kinds of drag-and-drop scenarios:

First, drag the mode immediately:

Inherit the required controls and override the Doevent function. In the uievent_buttondown,uievent_buttonup,uievent_mousemove three events directly according to the position of the mouse to change the coordinates and size of the control. Obviously this requires us to set the float property to true, and this way is simple to implement. I'm not going to write the code.

Second, not immediately drag-and-drop mode:

< Span style= "font-size:14px" >      1,   Inherit the required controls and override the Doevent function in uievent_buttondown, uievent_buttonup, uievent_mousemove three events, overriding Dopostpaint function finish drawing of Shadow

< Span style= "font-size:14px" >      2, Span style= "font-size:14px" >uievent_buttondown event call addpostpaint function Register this control

< Span style= "font-size:14px" > & nbsp    3, calculates the new control position in the uievent_mousemove event. and combine the old and new locations to call the Invalidate function refresh location (otherwise there will be a ghost)

4, in the Uievent_buttonup event call Removepostpaint anti-register himself. and refreshes the control.


I wrote a simple example, and the experiment was successful, and this is the experimental code:

void Cmodulepanecellui::D oevent (teventui& event) {if (event. Type = = Uievent_buttondown && isenabled ()) {if (::P tinrect (&m_rcitem, Event.ptmouse)) {m_ubuttonstate |= UIS Tate_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 &= ~uistate_captured;    cmodulepaneconfigui* pparent = static_cast<cmodulepaneconfigui*> (m_pparent);pP Arent->notifydrag (this); The Notifydrag function is a function of the Cmodulepaneconfigui container. and drag the effect itself does not matter 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;//The position of the current drag block and the location of the previous moment of the current drag block. Refresh Cduirect rcinvalidate = M_rcnewpos;m_rcnewpos = Rccurpos;rcinvalidate.join (M_rcnewpos); if (M_pManager) m_pManager- >invalidate (rcinvalidate); return;}} if (event. Type = = uievent_setcursor) {if (IsEnabled ()) {:: SetCursor (:: LoadCursor (NULL, Makeintresource (Idc_hand)); return;}} Clabelui::D oevent (event);} void Cmodulepanecellui::D opostpaint (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::D rawcolor (HDC, Rcupdate, 0xaa000000);}


This is the experimental effect, the blue part is the original button control. Black shadows are the process of being dragged. I control the shadow drawing range not exceeding the parent container in Dopostpaint.

This is just an experiment, I do not have the practical good-looking material to do, but proved the feasibility, the replacement material will become very good-looking. Cmodulepaneconfigui is a red container that implements the same magnetic block effect of a drag element as its own active layout, which does not matter with the drag effect itself. Cmodulepanecellui is a drag-and-drop element control. He can inherit whatever duilib control comes from.





Use this method. Ability to change Duilib's original code to support drag-and-drop functionality for the list control and the TreeView control. You can also define some special-purpose controls on your own.

Here is the idea of drawing drag-and-drop effects, and the detailed usage method is changeable.


Summarize:

This is the code I wrote before to finish a function, hoping to help the friends I need. If you have a bug in your code or have a better drag-and-drop implementation, please contact me or leave a message.


Redrain 2014.11.15


qq:491646717


Copyright notice: This article blog original articles, blogs, without consent, may not be reproduced.

Implementation of the Duilib Drag control feature (source code)

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: info-contact@alibabacloud.com 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.