Can anyone solve my Drawable conundrums ? - writing Composite classes to implement "Canvas" type control


#1

I have been for the past two weeks,  attempting to create my own "Canvas" class and supporting graphics classes for drawing shapes and text inside this canvas in order to replicate the API behaviour found in the XOJO framework , using the Drawable, DrawableText, DrawableRectangle, and possibly  DrawableComposite. 

However I am utterly flummoxed as to how best do this in JUCE so as to replicate XOJO API behaviour co-ordinate wise.

Basically whereas in JUCE we have various subclasses of Drawable for different shapes or text, plus a DrawableComposite for - presumably easily create groups of linked drawables that can be translated en-masse,   in XOJO we have: 

Canvas - the control on which we draw stuff 

Object2D - the base class form which shapes and text can be subclassed - eg RectShape, Arcshape, TextShape  ( similar to Drawable )

Group2D - a "container" for a group of Object2D subclasses -  ( similar in concept to DrawableComposite ? ) 

 

Group2D is a subclass of Object2D, just like DrawableComposite is a subclass or Drawable.

 

THE PROBLEM: 

=============

This might seem straightforward, BUT for how co-ordinates are dealt with in the XOJO framework ( which i need to replicate ). 

Both Object2D and Group2D  have X and Y setters and getters in which the X value is ALWAYS the horizontal distance from the CENTRE of the graphical object ( RectShape etc ) to the the LEFT of the one Canvas control on which the shapes etc are being drawn.  

This differs of course with Component classes where the setCentrePosition(..) method uses coordinates relative to the containing parent

class, NOT to the final master CANVAS control.

The XOJO framework is designed so that if several Object2D instances are added to a Group2D  then from then on, whenever the XY properties of the Group2D object are changed,  the objects inside get automatically translated - they move "in step". 

 

For example in XOJO  if I create an instance of RectShape ( subclassed from Object2D ) and set the X to 10, then doing a get_X() on the RectShape will return 10.  

 If I  then append this instance to a Group2D object and then set the X of the Group2D to 100 ( 100 pixels from the Canvas Left ) doing a get_X() again on the RectShape will now return 110 - since ALL  XOJO "drawable" coordinates ALWAYS measure from the centre of the Object2D to the left edge of the master CANVAS object. 

 

I have already written the classes and most of it works - except the objects dont draw in the right places. 

 

I have enclosed a trimmed down version of the class headers in a file and also another file with the relevant XOJO API documentation

 

  

//@@@@@@@@@@@@@@@@@@@@@@  Object2D    @@@@@@@@@@@@@@@@@@@@@@
class Object2D :  public virtual ReferenceCountedObject
{
 
  public:
    
    friend Group2D;
 
     virtual Drawable* getDrawable() = 0;
     virtual void draw (juce::Graphics& g, int offsetx, int offsety );
    
     double get_X();
     void set_X(double value);
     double get_Y();
     void set_Y(double value);    
    
     virtual ~Object2D() {};
     Object2D();
      
  private:
    
    Group2D* parent;
        
};



//@@@@@@@@@@@@@@@@@@@@@@    RectShape    @@@@@@@@@@@@@@@@@@@@@@
class RectShape : public Object2D , public DrawableRectangle
{
private:
protected:
    
    double width = 0;
    double height = 0;
public:
    
    REF(Object2D) create2DCopy();
    
    Drawable* getDrawable();
    void setDrawablePath( DrawablePath* d);
    
    
    PROPERTY_DOUBLE(Width)
    PROPERTY_DOUBLE(Height)
    
    bool Contains( double x, double y );
    RectShape();
    RectShape( RectShape* p );
    
    ~RectShape();
};


// note to JULES - 
Group2D inherits from DrawableRectangle not DrawableComposite, currently for debugging reasons and because it   seemed I needed a class that had "width" and "depth" in order for my calculations..  I'm sure theres a better  way than this.. 


//@@@@@@@@@@@@@@@@@@@@@@  Group2D  @@@@@@@@@@@@@@@@@@@@@@
class Group2D :  public DrawableRectangle, public Object2D
{
     private:
    
        ReferenceCountedArrayObject<(Object2D> group; 
    
        void getBounds (float& x, float& y, float& width, float& height) const;
    
     public:
    
        Group2D();
        ~Group2D();    
    
        int Count();
    
        REF(Object2D) Item( int index );
        void  Append( Object2D* aobject );  // ONCE APPENDED - all graphical objects are translated etc as a group ( affine style ) 
     
    
     double get_X();
     void set_X(double value);
     double get_Y();
     void set_Y(double value);    
    
    void draw( JUCE::Graphics& g , int offsetx, int offsety );
    void draw (JUCE::Graphics& g, const AffineTransform& transform) const;
          
    
    Drawable* getDrawable() { return this;  };    
    
    Drawable* createCopy() const;
  
    Rectangle<float> getDrawableBounds() const ;
    
};

//@@@@@@@@@@@@@@@@@@@@@@   Canvas    @@@@@@@@@@@@@@@@@@@@@@
class Canvas : public Component, public Control
{

 public:
     Canvas();
    
     void paint (GRAPHICSJ &g);
    
     virtual void  ON_EnableMenuItems() {};    
     virtual void  ON_GotFocus() {};
     virtual void  ON_LostFocus() {};
     virtual void  ON_MouseUp(int x, int y) {};
     virtual bool  ON_MouseDown(int x, int y) {return false; };
     virtual void  ON_MouseDrag(int x, int y) {};
    
     virtual void  ON_Paint(Graphics* g, ARRAYREF_INTR(RB_Rect) areas ) {} ;
    
     void mouseDown (const MouseEvent &e);
     void mouseUp (const MouseEvent &e);
     void mouseDrag (const MouseEvent &e);
    
     void     focusGained (Component::FocusChangeType cause);
     void     focusLost (Component::FocusChangeType cause);    
     virtual void mouseMove (const MouseEvent &e);
     virtual void mouseEnter (const MouseEvent &e);
     virtual void mouseExit (const MouseEvent &e);     
     void mouseDoubleClick (const MouseEvent &e);
     virtual void mouseWheelMove (const MouseEvent &e, const MouseWheelDetails & wheel);
     void     componentMovedOrResized (Component &component, bool wasMoved, bool wasResized); 
     void     parentSizeChanged ();
     static juce::Graphics* getJuceGraphics();
    
};