Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Scrollgroup and UserArea drawing artifacts

    SDK Help
    0
    3
    934
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • H
      Helper
      last edited by

      On 24/04/2018 at 12:45, xxxxxxxx wrote:

      User Information:
      Cinema 4D Version:   R19 
      Platform:   Windows  ;   
      Language(s) :     C++  ;

      ---------
      I am using a GeDialog containing some sort of "log" area to visualize some processing.
      This uses a GeUserArea inside of a scrollgroup.
      The idea is that the log window automatically scrolls to show the current line being processed, while still allowing the user to scroll back through the results to investigate the rest of the data when processing is completed.

      Unfortunately, I cannot seem to find a way to scroll without messing up the drawing of the userarea. Some artifacts happen as a result of the scrolling, resulting in the bottom "smearing" out, occasionally.

      Here is a simplified example containing only the necessary things to reproduce the issue.
      (It's a full plugin implementation which can be put in a single cpp file, I usually work with multiple header files and cpp files per object, but it seemed easier to provide the code as a single "block")

      The plugin consists of a CommandData that shows a dialog. Pressing the "Process" button will fill the "log" window with some dummy data to visualize the scrolling issue.

      What am I doing wrong ?
      Thanks in advance.

        
      // ========================  
      // Testing  
      // Dialog with scrollgroup and userarea  
      // ========================  
        
      #include "c4d.h"  
      #include "lib_clipmap.h"  
        
      // Dummy IDs - for demonstration purposes only  
      #define MYCOMMAND_PLUGIN_ID    1999999  
        
      enum {  
        IDC_PROCESS = 5000,  
        IDC_SCROLLGROUPV,  
        IDC_USERAREA  
      };  
        
      #define kSpacing 2  
        
      // ====================================  
      // UserArea  
      // ====================================  
        
      struct mydata {  
        String    text;  
        Int32    current;  
        Int32    total;  
        
        mydata(const String& s, const Int32& t) :  
            text(s), current(0), total(t)  
        {}  
      };  
        
      class MyUserArea : public GeUserArea  
      {  
        INSTANCEOF(MyUserArea, GeUserArea)  
        
      public:  
        MyUserArea();  
        virtual ~MyUserArea();  
        
        virtual Bool Init(void);  
        virtual Bool GetMinSize(Int32& w, Int32& h);  
        //virtual void Sized(Int32 w, Int32 h);  
        virtual Bool InitValues(void);  
        virtual void DrawMsg(Int32 x1, Int32 y1, Int32 x2, Int32 y2, const BaseContainer& msg);  
        
        void AddData(const mydata& newdata);  
        void UpdateCurrent(const Int32& i, const Int32& newcurrent);  
        
      private:  
        AutoAlloc<GeClipMap>        mClipmap;  
      public:  
        Int32                        mTextHeight;  
        maxon::BaseArray<mydata>    mData;  
      };  
        
      MyUserArea::MyUserArea(void) : GeUserArea(), mTextHeight(0) {}  
      MyUserArea::~MyUserArea(void) {}  
        
      Bool MyUserArea::Init(void)  
      {  
        // calculate the font's textheight  
        mClipmap->Init(100, 100, 32);  
        mClipmap->BeginDraw();  
        mClipmap->SetFont(nullptr); // use default font  
        mTextHeight = mClipmap->GetTextHeight();  
        mClipmap->EndDraw();  
        return TRUE;  
      }  
        
      Bool MyUserArea::GetMinSize(Int32& w, Int32& h)  
      {  
        w = 100;  
        h = (mTextHeight + kSpacing) * Max(1, (Int32)mData.GetCount());  
        
        return TRUE;  
      }  
        
      Bool MyUserArea::InitValues(void)  
      {  
        return TRUE;  
      }  
        
      void MyUserArea::DrawMsg(Int32 x1, Int32 y1, Int32 x2, Int32 y2, const BaseContainer& msg)  
      {  
        if (!mClipmap)  
            return;  
        
        OffScreenOn();  
        SetClippingRegion(x1, y1, x2, y2);  
        
        Int32 w = x2 - x1;  
        Int32 h = y2 - y1;  
        
        mClipmap->Init(w, h, 32);  
        mClipmap->BeginDraw();  
        
        // draw the background  
        Vector bkcolor = GetViewColor(VIEWCOLOR_C4DBACKGROUND);  
        bkcolor *= 255; // vector color is defined as values 0..1, we need 0..255 in GeClipMap  
        mClipmap->SetColor(SAFEINT32(bkcolor.x), SAFEINT32(bkcolor.y), SAFEINT32(bkcolor.z), 255);  
        mClipmap->FillRect(x1, y1, x2, y2);  
        
        mClipmap->SetFont(nullptr); // use default font  
        
        Int32 barlength = SAFEINT32(w * 0.5) - (2 * kSpacing);  
        Int32 posx1 = kSpacing;  
        Int32 posx2 = kSpacing + SAFEINT32(w * 0.5);  
        Int32 posy = kSpacing;  
        
        Vector textColor(255);  
        Vector backColor(0);  
        Vector color(0, 255, 0);  
        
        for (Int32 i = 0; i < (Int32)mData.GetCount(); ++i)  
        {  
            mClipmap->SetColor(SAFEINT32(textColor.x), SAFEINT32(textColor.y), SAFEINT32(textColor.z), 255);  
            mClipmap->TextAt(posx1, posy, mData _.text);  
        
            mClipmap->SetColor(SAFEINT32(backColor.x), SAFEINT32(backColor.y), SAFEINT32(backColor.z), 255);  
            mClipmap->FillRect(posx2 + 1, posy + 1, posx2 + barlength, posy + mTextHeight - 1);  
        
            mClipmap->SetColor(SAFEINT32(color.x), SAFEINT32(color.y), SAFEINT32(color.z), 255);  
            Int32 value = (mData _.total != 0) ? barlength * mData _.current / mData _.total : 0;  
            mClipmap->FillRect(posx2 + 1, posy + 1, posx2 + value, posy + mTextHeight - 1);  
        
            posy += mTextHeight + kSpacing;  
        }  
        
        mClipmap->EndDraw();  
        DrawBitmap(mClipmap->GetBitmap(), 0, y1, w, h, 0, y1, w, h, BMP_ALLOWALPHA);  
      }  
        
      void MyUserArea::AddData(const mydata& newdata)  
      {  
        mData.Append(newdata);  
        LayoutChanged();  
      }  
        
      void MyUserArea::UpdateCurrent(const Int32& i, const Int32& newcurrent)  
      {  
        if ((i < 0) || (i >= mData.GetCount()))  
            return;  
        mData _.current = newcurrent;  
        Redraw();  
      }  
        
      // ====================================  
      // Dialog  
      // ====================================  
        
      class MyDialog : public GeDialog  
      {  
        INSTANCEOF(MyDialog, GeDialog)  
        
      public:  
        MyDialog(void);  
        virtual ~MyDialog(void);  
        
        virtual Bool CreateLayout(void);  
        virtual Bool InitValues(void);  
        virtual Bool Command(Int32 id, const BaseContainer& msg);  
        
        MyUserArea    mUserArea;  
      };  
        
      MyDialog::MyDialog(void) : GeDialog() {}  
      MyDialog::~MyDialog(void) {}  
        
      Bool MyDialog::CreateLayout(void)  
      {  
        Bool res = GeDialog::CreateLayout();  
        
        SetTitle("Convert PolyGnome libraries");  
        
        GroupBegin(0, BFH_SCALEFIT | BFV_SCALEFIT, 1, 0, String(), 0);  
        {  
            GroupBegin(0, BFH_SCALEFIT, 2, 0, String(), 0);  
            {  
                AddStaticText(0, BFH_LEFT, 100, 0, String("Some gadgets"), 0);  
                AddStaticText(0, BFH_LEFT | BFH_SCALEFIT, 100, 0, String("... Dummy ..."), 0);  
            }  
            GroupEnd();  
        
            GroupBegin(0, BFH_SCALEFIT, 2, 0, String(), 0);  
            {  
                AddStaticText(0, BFH_LEFT, 100, 0, String("Other gadgets"), 0);  
                AddStaticText(0, BFH_LEFT | BFH_SCALEFIT, 100, 0, String("... More dummy ..."), 0);  
            }  
            GroupEnd();  
        
            // status area displaying the list of old-format labraries  
            // and progress of conversion (per library)  
            ScrollGroupBegin(IDC_SCROLLGROUPV, BFH_SCALEFIT | BFV_SCALEFIT, SCROLLGROUP_VERT | SCROLLGROUP_NOBLIT, 100, 10);  
            C4DGadget* ua = AddUserArea(IDC_USERAREA, BFH_SCALEFIT | BFV_SCALEFIT, 100, 10);  
            if (ua)  
                AttachUserArea(mUserArea, ua);  
            GroupEnd();  
        
            AddButton(IDC_PROCESS, BFH_CENTER, 200, 0, "Process");  
        }  
        GroupEnd();  
        
        return res;  
      }  
        
      Bool MyDialog::InitValues(void)  
      {  
        return TRUE;  
      }  
        
      Bool MyDialog::Command(Int32 id, const BaseContainer& msg)  
      {  
        switch (id)  
        {  
            case IDC_PROCESS:  
                {  
                    mUserArea.mData.Reset();  
                    // process some data  
                    String txt[] = { "Entry 1", "Entry 2", "Entry 3", "Entry 4", "Entry 5", "Entry 6", "Entry 7" };  
                    Int32 count[] = { 10, 15, 10, 7, 5, 8, 6 };  
        
                    for (Int32 i = 0; i < 7; ++i)  
                    {  
                        mydata data(txt _, count _);  
                        mUserArea.AddData(data);  
        
                        // set the focus on the current data entry  
                        {  
                            Int32 sx1, sx2, sy1, sy2;  
                            GetVisibleArea(IDC_SCROLLGROUPV, &sx1, &sy1, &sx2, &sy2);  
                            Int32 currentY = mUserArea.mTextHeight * i;  
                            if ((currentY < sy1) || (currentY > sy2))  
                            {  
                                sy1 = currentY;  
                                sy2 = sy1 + mUserArea.mTextHeight;  
                                SetVisibleArea(IDC_SCROLLGROUPV, sx1, sy1, sx2, sy2); // -> artifacts  
                            }  
                        }  
                        mUserArea.LayoutChanged();  
        
                        // update the data entry  
                        for (Int32 j = 0; j <= count _; ++j)  
                        {  
                            mUserArea.UpdateCurrent(i, j);  
                            GeSleep(50);  
                        }  
                    }  
                }  
                break;  
        }  
        return TRUE;  
      }  
        
      // ====================================  
      // CommandData  
      // ====================================  
        
      class MyCommand : public CommandData  
      {  
        INSTANCEOF(MyCommand, CommandData)  
        
      public:  
        MyDialog    mDlg;  
        
      public:  
        virtual Bool Execute(BaseDocument* doc);  
        virtual Bool ExecuteSubID(BaseDocument* doc, Int32 subid);  
        virtual Int32 GetState(BaseDocument* doc);  
        //virtual Bool GetSubContainer(BaseDocument* doc, BaseContainer& submenu);  
        virtual Bool RestoreLayout(void* secret);  
        virtual Bool Message(Int32 type, void* data);  
      };  
        
      Bool MyCommand::Execute(BaseDocument* doc)  
      {  
        if (!mDlg.IsOpen())  
            mDlg.Open(DLG_TYPE_ASYNC, MYCOMMAND_PLUGIN_ID, -1, -1, 300, 75, 0);  
        return TRUE;  
      }  
        
      Bool MyCommand::ExecuteSubID(BaseDocument* doc, Int32 subid)  
      {  
        return TRUE;  
      }  
        
        
      Int32 MyCommand::GetState(BaseDocument* doc)  
      {  
        return CMD_ENABLED;  
      }  
        
      Bool MyCommand::RestoreLayout(void* secret)  
      {  
        mDlg.RestoreLayout(MYCOMMAND_PLUGIN_ID, 0, secret);  
        return TRUE;  
      }  
        
      Bool MyCommand::Message(Int32 type, void* data)  
      {  
        return SUPER::Message(type, data);  
      }  
        
      Bool RegisterMyCommand(void)  
      {  
        return RegisterCommandPlugin(MYCOMMAND_PLUGIN_ID, "Testing", 0, AutoBitmap("icon.png"), "Test", NewObjClear(MyCommand));  
      }  
        
      // ====================================  
      // Plugin Main   
      // ====================================  
      Bool PluginStart(void)  
      {  
        RegisterMyCommand();  
        return TRUE;  
      }  
      void PluginEnd(void)   
      {  
      }  
      Bool PluginMessage(Int32 id, void * data)  
      {  
        switch (id) {  
        case C4DPL_INIT_SYS:  
            if (!resource.Init())  
                return FALSE;  
            return TRUE;  
        case C4DMSG_PRIORITY:  
            return TRUE;  
        case C4DPL_BUILDMENU:  
            break;  
        case C4DPL_ENDACTIVITY:  
            return TRUE;  
        }  
        return FALSE;  
      }  
        
      

      1 Reply Last reply Reply Quote 0
      • H
        Helper
        last edited by

        On 25/04/2018 at 01:34, xxxxxxxx wrote:

        Hi C4DS,

        The main issue with your code is you actually draw into a bitmap which always equals to the size of the GeUserArea. And you do all your drawing from the top of this bitmap.
        Then you scroll this GeUserArea (so you ask, to draw a part of the bitmap which is not drawn)

        In order to fix it simply do:
        mClipmap->Init(w, y2, 32);

        Cheers,
        Maxime

        1 Reply Last reply Reply Quote 0
        • H
          Helper
          last edited by

          On 25/04/2018 at 02:16, xxxxxxxx wrote:

          Aaargh !!!
          Silly me. Never thought about that.
          Thanks for pointing it out Maxime.

          1 Reply Last reply Reply Quote 0
          • First post
            Last post