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;
}