1 / 38

Creating and Improving Sketcher Document - Remember and Redraw Elements, Multiple Views, Scrolling

Learn how to create and improve a Sketcher Document, remember and redraw elements, handle multiple views, and enable scrolling. Includes tips on deleting and moving shapes, highlighting elements, implementing a context menu, and more.

collazoe
Download Presentation

Creating and Improving Sketcher Document - Remember and Redraw Elements, Multiple Views, Scrolling

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Chapter 17Creating the Document and Improving the View

  2. The Sketcher Document Does Not Remember What You Have Drawn • Whenever you resize the window, the elements disappear! • Inspect class CSketcherDoc, you only have the following data members: protected: // Current element type ElementType m_Element; // Current drawing color COLORREF m_Color;

  3. Store Elements in a List (P.1010) • Data Member: • std::list<CElement*> m_ElementList; • Member Function: • void AddElement(CElement* pEleemnt) { m_ElementList.push_back(pElement); } • To Make CSketcherDoc recognize these data types: • #include <list> • #include "Elements.h"

  4. Destroy the elements in the destructor CSketcherDoc::~CSketcherDoc() { // Delete the element pointed to by each list entry for (auto iter = m_ElementList.begin(); iter != m_ElementList.end(); ++iter) delete *iter; // Finally delete all pointers m_ElementList.clear(); } • The auto keyword specifies the correct data type according to the initial value.

  5. const_iterator • The list is owned by the list, as a protected data member. You can’t access it directly from the view. class CSketcherDoc : public CDocument { // Operations public: // Add an element to the list void AddElement(CElement* pElement) { m_ElementList.push_back(pElement); } // Get list begin iterator std::list<CElement*>::const_iterator begin() const { return m_ElementList.begin(); } // Get list end iterator std::list<CElement*>::const_iterator end() const { return m_ElementList.end(); }

  6. Not every element needs to be redrawn

  7. Drawing only two elements improves the performance

  8. Determine which element needs to be redrawn • RectVisible() determines whether a rectangle area overlaps the area that Windows must redraw. void CSketcherView::OnDraw(CDC* pDC) { CSketcherDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; CElement* pElement(nullptr); for (auto iter = pDoc->begin(); iter != pDoc->end(); ++iter) { pElement = *iter; if (pDC->RectVisible(pElement->GetBoundRect())) pElement->Draw(pDC); // If the element is visible // ... draw it } }

  9. Adding an Element to the Document • OnLButtonUP(), add the temporary element to the document. void CSketcherView::OnLButtonUp(UINT nFlags, CPoint point) { if (this == GetCapture()) ReleaseCapture(); // Stop capturing mouse messages // Make sure there is an element if (m_pTempElement) { // Call a document class function to store the element // pointed to by m_pTempElement in the document object GetDocument()->AddElement(m_pTempElement); InvalidateRect(nullptr); // Redraw the current window m_pTempElement = 0; // Reset the element pointer } } • Note that the statement deleting m_pTempElement has been removed. • The document destructor will handle this.

  10. Exercising the Document • Now the document remembers all the elements you have drawn. • Try to draw “The Happy Programmer”, and then resize the window.

  11. Creating Multiple Views of a Document

  12. Updating Multiple Views • Right now, if you draw in a view, the other view will not be updated automatically. • Each view is acting independently of the others. • They don’t know what’s happening in other views. void CSketcherView::OnLButtonUp(UINT nFlags, CPoint point) { if (this == GetCapture()) ReleaseCapture(); // Stop capturing mouse messages // Make sure there is an element if (m_pTempElement) { // Call a document class function to store the element // pointed to by m_pTempElement in the document object GetDocument()->AddElement(m_pTempElement); // Tell the views GetDocument()->UpdateAllViews(nullptr, 0, m_pTempElement); InvalidateRect(nullptr); // Redraw the current window m_pTempElement = 0; // Reset the element pointer } }

  13. UpdateAllViews() Useful when the current view is already up to date

  14. Add an override for OnUpdate() • Right-click CSketcherView and choose Override in the Properties Window. • <Add> OnUpdate() void CSketcherView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) { if (pHint) InvalidateRect( static_cast<CElement*>(pHint)->GetBoundRect()); else InvalidateRect(nullptr); } • Now both views are updated.

  15. Scrolling Views • Change the base class for CSketcherView from CView to CScrollView. • In SketcherView.h: • class CSketcherView : public CScrollView • In SketcherView.cpp: • IMPLEMENT_DYNCREATE(CSketcherView, CScrollView) • BEGIN_MESSAGE_MAP(CSketcherView, CScrollView)

  16. SetScrollSizes()

  17. Specify the size and scrolling distance override void CSketcherView::OnInitialUpdate() { CScrollView::OnInitialUpdate(); // Define document size CSize DocSize(20000, 20000); // Set mapping mode and document size SetScrollSizes(MM_TEXT, DocSize, CSize(500,500), CSize(50,50)); }

  18. Exercise to Demo • Use a vector instead of a list (as in Chapter 17) to store the elements.

  19. Deleting and Moving Shapes • Highlighting Elements • Implementing a Context Menu • Servicing the Menu Messages • Deleting an Element • Moving an Element

  20. Context Menus • Right-click will select an element and pops up a menu relating to actions that can be performed on that object. • For different elements, different menus will be displayed. That’s why it is call a “context-sensitive menu”.

  21. You Will Need Two Context Menus • When there is an element under the mouse cursor: • Move and Delete • When there isn’t: • Menu items from the Element and Color menus

  22. Implementing a Context Menu • Resource View • Right-click the Menu folder • Insert Menu • The default ID is IDR_MENU1 • Select this new menu, and pressing Alt-Enter to display the Properties window. • Change its resource ID to be IDR_ELEMENT_MENU.

  23. Create items on the menu • Editor pane. • The caption won’t be displayed to the user. • Add the Move and Delete items to the element pop-up. • The default IDs are ID_ELEMENT_MOVE and ID_ELEMENT_DELETE.

  24. IDR_NOELEMENT_MENU • Creating another menu with ID IDR_NOELEMENT_MENU • The caption no element won’t be shown to users. • Copy all items from the Element menu and the Color menu. • Click the first item and then click the last item while holding down the Shift key. • Press Ctrl+C to copy. • Click the no element menu. • Press Ctrl+V to paste. • The copied menu items will have the same IDs as the originals. • You may have a separator between the Element menu items and the Color menu items.

  25. Associating a Menu with a Class • AddMenu() function in an CContextManager object. void CSketcherApp::PreLoadState() { // Delete the remarked portion on P.1023 GetContextMenuManager()->AddMenu( _T("Element menu"), IDR_ELEMENT_MENU); GetContextMenuManager()->AddMenu( _T("No element menu"), IDR_NOELEMENT_MENU); } The _T() macro selects the correct type for the string, depending on whether or not the _UNICODE symbol is defined.

  26. Identifying a Selected Element • An element is a sketch will be selected whenever the mouse cursor is within the bounding rectangle. • Add FindElement() to the document class as a public member: // Finds the element under the point CElement* CSketcherDoc::FindElement(const CPoint& point) const { for (auto rIter = m_ElementList.rbegin(); rIter != m_ElementList.rend(); ++rIter) if ((*rIter)->GetBoundRect().PtInRect(point)) return *rIter; return nullptr; }

  27. Check which element is selected whenever the mouse moves • Use a variable m_pSelected to store the address of the element under the mouse cursor, or nullptr if there isn’t one. void CSketcherView::OnMouseMove(UINT nFlags, CPoint point) { // Define a Device Context object for the view CClientDC aDC(this); // DC is for this view if((nFlags & MK_LBUTTON) && (this == GetCapture()) ) { // Code as before ... } else { // We are not creating an element, so select an element CSketcherDoc* pDoc=GetDocument(); m_pSelected = pDoc->FindElement(point); } }

  28. m_pSelected • Add m_pSelected as a protected member of the CSketcherView class as type CElement*. • Initialize this member to nullptr in the view class constructor.

  29. ShowPopupMenu() • Look at CSketcherView class implementation. In OnRButtonUP() handler, it already calls the OnContextMenu() handler to show a popup menu. • Modify OnContextMenu(): void CSketcherView::OnContextMenu(CWnd* pWnd, CPoint point) { #ifndef SHARED_HANDLERS // theApp.GetContextMenuManager()->ShowPopupMenu( // IDR_POPUP_EDIT, point.x, point.y, this, TRUE); if (m_pSelected) theApp.GetContextMenuManager()->ShowPopupMenu( IDR_ELEMENT_MENU, point.x, point.y, this); else theApp.GetContextMenuManager()->ShowPopupMenu( IDR_NOELEMENT_MENU, point.x, point.y, this); #endif }

  30. Try It Out • Build and execute Sketcher. • Right-click the mouse, or press Shift+F10 • A context menu will be displayed. • If there are no elements under the cursor, the second context pop-up appears, enabling you to change the element type and color. • If there is an element under the cursor, the first context menu will appear with Move and Delete on it. • It won’t do anything at this moment.

  31. Highlighting Elements • You should draw the element under the cursor with a different color, so that users can make sure the right-click will be applied on which element. • Change the Draw() member function for an element. • Pass an extra argument • m_pSelected, which is the currently-selected element • Compare it with this pointer, and draw in a special color if this == m_pSelected.

  32. CSketcherView::OnDraw() void CSketcherView::OnDraw(CDC* pDC) { CSketcherDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; CElement* pElement(nullptr); for (auto iter = pDoc->begin(); iter != pDoc->end(); ++iter) { pElement = *iter; // If the element is visible if (pDC->RectVisible(pElement->GetBoundRect())) pElement->Draw(pDC, m_pSelected); // ... draw it } }

  33. Draw() in CElement class • Modify the definition of Draw() in the base class CElement class CElement : public CObject { protected: int m_PenWidth; // Pen width COLORREF m_Color; // Color of an element CElement(); public: virtual ~CElement(); // Virtual draw operation virtual void Draw(CDC* pDC, CElement* pElement=nullptr) {} // Get the bounding rectangle for an element CRect GetBoundRect(void); }; • Remember to remove the virtual CElement::Draw() in Elements.cpp

  34. Change the CLine class definition class CLine : public CElement { public: virtual ~CLine(void); // Function to display a line virtual void Draw(CDC* pDC, CElement* pElement=nullptr); // Constructor for a line object CLine(const CPoint& start, const CPoint& end, COLORREF aColor); protected: CPoint m_StartPoint; // Start point of line CPoint m_EndPoint; // End point of line CLine(void); // Default constructor should not be used };

  35. Implementation of CLine::Draw() void CLine::Draw(CDC* pDC, CElement* pElement) { // Create a pen for this object and // initialize it to the object color and line width of 1 pixel CPen aPen; if(!aPen.CreatePen(PS_SOLID, m_PenWidth, this==pElement ? SELECT_COLOR : m_Color)) { // Pen creation failed. Abort the program AfxMessageBox(_T("Pen creation failed drawing a line"), MB_OK); AfxAbort(); } CPen* pOldPen = pDC->SelectObject(&aPen); // Select the pen // Now draw the line pDC->MoveTo(m_StartPoint); pDC->LineTo(m_EndPoint); pDC->SelectObject(pOldPen); // Restore the old pen } In SketcherConstants.h: const COLORREF SELECT_COLOR = RGB(255,0,180); Apply the same change on CRectangle, CCircle, and CCurve.

  36. Tell MFC which elements to re-draw void CSketcherView::OnMouseMove(UINT nFlags, CPoint point) { // Define a Device Context object for the view CClientDC aDC(this); // DC is for this view if((nFlags & MK_LBUTTON) && (this == GetCapture()) ) { // Code as before } else { // We are not creating an element, so select an element CSketcherDoc* pDoc=GetDocument(); CElement* pOldSelected(m_pSelected); m_pSelected = pDoc->FindElement(point); if (m_pSelected != pOldSelected) { if (m_pSelected) InvalidateRect(m_pSelected->GetBoundRect(), FALSE); if (pOldSelected) InvalidateRect(pOldSelected->GetBoundRect(), FALSE); pDoc->UpdateAllViews(nullptr); } } } • If both pOldSelected and m_pSelected contain a valid address, the element has been already highlighted. • If they are both zero, nothing is highlighted. • So, you need to do something only when m_pSelected is not equal to pOldSelected. Not to erase the background Now try it out. The highlighted element is drawn in magenta.

  37. Deleting an Element void CSketcherView::OnElementDelete() { if (m_pSelected) { CSketcherDoc* pDoc = GetDocument(); // Get the document ptr pDoc->DeleteElement(m_pSelected); // Delete the element pDoc->UpdateAllViews(nullptr); // Redraw all the views m_pSelected = nullptr; // Reset selected element ptr } }

  38. DeleteElement() • Added as a public member of CSketcherDoc (P.1031) void CSketcherDoc::DeleteElement(CElement* pElement) { if (pElement) { // Remove the pointer from the list m_ElementList.remove(pElement); // Delete the element from the heap delete pElement; } } List Heap CElement

More Related