[Closed] [SDK] Register Dialog for docking
the MXS provides struct (cui) methods to Register Dialog (Rollout or Rollout Floater):
cui.RegisterDialogBar
cui.UnegisterDialogBar
I can’t find anything about this mechanics in MAX SDK. Does anyone has an idea what it does do? I’m sure I miss something and it must some methods to do the same via SDK
thanks
What I guess is – the system creates CUIFrameWindow and adds original Rollout (Floater) as a child. Anything smarter?
here’s my sdk version of meshtocode which i did specifically to create a dockable toolbar
#include "pgTools.h"
//#include "types.h"
#include MAXSCRIPT_INCLUDE
#include "utilfunc.h"
#include "common.h"
#include "MaxIcon.h"
#define MESHTOCODE_UTIL_CLASS_ID Class_ID(PG_CLASS_ID, 0x25ce684f)
#define MESHTOCODE_UTIL_INTERFACE_ID Interface_ID(PG_CLASS_ID, 0x70111fa8)
#define TBITEM(type, pIcon, cmd) \
ToolButtonItem(type,pIcon,GetCUIFrameMgr()->GetImageSize(),GetCUIFrameMgr()->GetImageSize(),GetCUIFrameMgr()->GetButtonWidth(),GetCUIFrameMgr()->GetButtonHeight(),cmd,0)
#define TBMACRO(md) \
ToolMacroItem(0, GetCUIFrameMgr()->GetButtonHeight(), md)
//#define DISABLE_FB_INTERFACE
//********************************************************************************************
class CUIFramePtr
{
public:
ICUIFrame* frame;
CUIFramePtr( HWND hwnd ) { frame = (hwnd == NULL ? NULL : GetICUIFrame(hwnd)); }
CUIFramePtr( ICUIFrame* frame ) { this->frame = frame; }
~CUIFramePtr() { if( frame != NULL ) ReleaseICUIFrame(frame); }
ICUIFrame* operator->() { return frame; }
ICUIFrame* operator=( ICUIFrame* ptr ) { frame = ptr; return frame; }
BOOL operator==( ICUIFrame* ptr ) { return frame==ptr; }
BOOL operator!=( ICUIFrame* ptr ) { return frame!=ptr; }
};
//********************************************************************************************
class ToolbarPtr
{
public:
ICustToolbar* toolbar;
ToolbarPtr( HWND hwnd ) { toolbar = (hwnd == NULL ? NULL : GetICustToolbar(hwnd)); }
ToolbarPtr( ICustToolbar* toolbar ) { this->toolbar = toolbar; }
~ToolbarPtr() { if( toolbar!=NULL ) ReleaseICustToolbar( toolbar ); }
ICustToolbar* operator->() { return toolbar; }
ICustToolbar* operator=( ICustToolbar* ptr ) { toolbar=ptr; return toolbar; }
BOOL operator==( ICustToolbar* ptr ) { return toolbar==ptr; }
BOOL operator!=( ICustToolbar* ptr ) { return toolbar!=ptr; }
// adds a spacer that can be used as the first entry as separator tool is bust in that respect
void AddSpacer(int width) { toolbar->AddTool(ToolOtherItem(_T("static"),width,16,0)); }
};
class MaxNodeToCode
{
virtual BOOL Enables(INode* selection) = 0;
virtual void WriteToFile(FILE* fstream, cstr& varname, INode* node, BOOL assignSMG) = 0;
virtual tstr& FilterStr() = 0;
virtual tstr& DropDownStr() = 0;
};
//*****************************************************************************************
static HIMAGELIST m2cppImages = NULL;
//**************************************************************************************
// MeshToCodeUtil
//*****************************************************************************************
class MeshToCodeUtil : public UtilityObj
{
public:
IUtil *iu;
Interface *ip;
HWND hPanel; // utility rollout panel
HWND toolHWND; // our floating tool
// Constructor/Destructor
MeshToCodeUtil() : iu(NULL), ip(NULL), toolHWND(NULL), hPanel(NULL) { }
~MeshToCodeUtil() {}
void Init(HWND hWnd) {}
void Destroy(HWND hWnd) {}
void DeleteThis() {}
// stuff for the utility panel
void BeginEditParams(Interface *ip,IUtil *iu);
void EndEditParams(Interface *ip,IUtil *iu);
// tool window handling routines
void CreateNewWindow();
void DestroyWindow();
// INT_PTR NotifyHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
ICUIFrame* GetICUIFrame() { return ::GetICUIFrame(GetParent(toolHWND)); }
BOOL ToolWindowIsOpen() { return GetParent(toolHWND) ? TRUE : FALSE; }
void DoMeshToCode(BOOL assignSMG);
void WriteMeshToCPPFile(FILE* fstream, cstr& varname, Mesh& mesh, BOOL assignSMG);
};
// our util global static
static MeshToCodeUtil m2cObj;
//*****************************************************************************************
// the class descriptor
//*****************************************************************************************
class MeshToCodeUtilClassDesc : public ClassDesc2
{
public:
int IsPublic() { return TRUE; }
void * Create(BOOL loading=FALSE) { return &m2cObj; }
const TCHAR * ClassName() { return GetString(IDS_MESHTOCODE_CLASS_NAME); }
SClass_ID SuperClassID() { return UTILITY_CLASS_ID; }
Class_ID ClassID() { return MESHTOCODE_UTIL_CLASS_ID; }
const TCHAR* Category() { return GetString(IDS_CATEGORY); }
const TCHAR* InternalName() { return _T("MeshToCodeUtil"); }
HINSTANCE HInstance() { return hInstance; } // returns owning module handle
};
static MeshToCodeUtilClassDesc MeshToCodeUtilDesc;
ClassDesc2* GetMeshToCodeUtilDesc() { return &MeshToCodeUtilDesc; }
//*****************************************************************************************
// function publishing, make some basic functionality available to mxs mostly so we can call our
// utility from the ui via a macroscript without switching to the utility panel first !
//**************************************************************************************
#ifndef DISABLE_FB_INTERFACE
class MeshToCodeUtilObjFPInterface : public FPStaticInterface
{
public:
DECLARE_DESCRIPTOR(MeshToCodeUtilObjFPInterface);
enum functionID { kOpenWindow, kCloseWindow, kIsOpen };
BEGIN_FUNCTION_MAP
VFN_0(kOpenWindow, OpenWindow)
VFN_0(kCloseWindow, CloseWindow)
FN_0(kIsOpen, TYPE_BOOL, IsOpen)
END_FUNCTION_MAP
void OpenWindow() { m2cObj.CreateNewWindow(); }
void CloseWindow() { m2cObj.DestroyWindow(); }
bool IsOpen() { return IsWindowOpen(); }
static bool IsWindowOpen() { return windowOpen; }
static void SetWindowOpen(bool val) { windowOpen = val; GetCUIFrameMgr()->SetMacroButtonStates(false); }
private:
static bool windowOpen;
};
//**************************************************************************************
// create the global static instance
static MeshToCodeUtilObjFPInterface theMeshToCodeUtilObjFPInterface(MESHTOCODE_UTIL_INTERFACE_ID, _T("MeshToCodeUtilOps"),
IDS_TESTING_CLASS_NAME, NULL, FP_CORE,
MeshToCodeUtilObjFPInterface::kOpenWindow, _T("openWindow"), -1, TYPE_VOID, FP_NO_REDRAW, 0,
MeshToCodeUtilObjFPInterface::kCloseWindow, _T("closeWindow"), -1, TYPE_VOID, FP_NO_REDRAW, 0,
MeshToCodeUtilObjFPInterface::kIsOpen, _T("isOpen"), -1, TYPE_BOOL, FP_NO_REDRAW, 0, p_end);
bool MeshToCodeUtilObjFPInterface::windowOpen = false;
#endif
//**************************************************************************************
static INT_PTR CALLBACK MeshToCodeUtilPanelProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
m2cObj.Init(hWnd); // can we delete these ?
break;
case WM_DESTROY:
m2cObj.Destroy(hWnd); // can we delete these ?
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_NEW_FLOATER_BTN:
m2cObj.CreateNewWindow();
break;
case IDOK:
case IDCANCEL:
DestroyWindow(hWnd);
break;
}
break;
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MOUSEMOVE:
m2cObj.ip->RollupMouseMessage(hWnd,msg,wParam,lParam);
break;
default:
return FALSE;
}
return TRUE;
}
//**************************************************************************************
// MsgHandler
//**************************************************************************************
class MsgHandler : public CUIFrameMsgHandler
{
public:
int ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam);
};
static MsgHandler theMsgHandler;
//**************************************************************************************
#define TBID_ASSIGN_SMG_CHK 20001
#define TBID_MESH_TO_CPP_BTN 20002
int MsgHandler::ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_CLOSE:
{
m2cObj.DestroyWindow();
return TRUE;
}
case WM_COMMAND:
switch (LOWORD(wParam))
{
case TBID_MESH_TO_CPP_BTN:
{
m2cObj.DoMeshToCode(IsDlgButtonChecked(m2cObj.toolHWND,TBID_ASSIGN_SMG_CHK));
return TRUE;
}
default:
return FALSE;
}
}
return FALSE;
}
//**************************************************************************************
// PropmanUtil, code for our utility starts here
//*****************************************************************************************
void MeshToCodeUtil::BeginEditParams(Interface *ip,IUtil *iu)
{
this->iu = iu;
this->ip = ip;
hPanel = ip->AddRollupPage(hInstance,MAKEINTRESOURCE(IDD_MESHTOCODE_PANEL),MeshToCodeUtilPanelProc,
GetString(IDS_PARAMS), 0);
}
//********************************************************************************************
void MeshToCodeUtil::EndEditParams(Interface *ip,IUtil *iu)
{
this->iu = NULL;
ip->DeleteRollupPage(hPanel);
hPanel = NULL;
}
//*****************************************************************************************
void MeshToCodeUtil::CreateNewWindow()
{
if(ToolWindowIsOpen()) return;
ip = GetCOREInterface(); // catches any script calls
LoadImages(m2cppImages,IDB_MESHTOCPP,16,16,1);
HWND parentHWND = ip->GetMAXHWnd();
// create the frame window
HWND frameHWND = CreateCUIFrameWindow(parentHWND, _T("Mesh To C++"), 0, 0, 250, 100);
CUIFramePtr theFrame(frameHWND);
theFrame->SetContentType(CUI_TOOLBAR);
theFrame->SetPosType(CUI_HORIZ_DOCK | CUI_VERT_DOCK | CUI_FLOATABLE | CUI_SM_HANDLES);
// create the tool window
toolHWND = CreateWindow(CUSTTOOLBARWINDOWCLASS, NULL, WS_CHILD|WS_VISIBLE,0,0,250,100, frameHWND, NULL, hInstance,NULL);
ToolbarPtr theToolBar(toolHWND);
theToolBar->LinkToCUIFrame(frameHWND, &theMsgHandler);
theToolBar->SetBottomBorder(FALSE);
theToolBar->SetTopBorder(FALSE);
theToolBar->SetImage(m2cppImages);
theToolBar.AddSpacer(8);
theToolBar->AddTool(ToolOtherItem(_T("button"),80,16,TBID_ASSIGN_SMG_CHK,WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX,
CENTER_TOOL_VERTICALLY,_T("Assign SMG")));
theToolBar->AddTool(ToolButtonItem(CTB_PUSHBUTTON,0,0,0,0,16,16,23,22,TBID_MESH_TO_CPP_BTN));
// resize our frame to fit tool bar
SIZE sz;
theToolBar->GetFloatingCUIFrameSize(&sz);
SetWindowPos(frameHWND, HWND_TOP,0,0,sz.cx,sz.cy,SWP_NOMOVE|SWP_NOREDRAW|SWP_NOZORDER);
GetCUIFrameMgr()->DockCUIWindow (frameHWND, CUI_TOP_DOCK);
GetCUIFrameMgr()->RecalcLayout(TRUE);
#ifndef DISABLE_FB_INTERFACE
MeshToCodeUtilObjFPInterface::SetWindowOpen(true);
#endif
}
//**************************************************************************************
// destroy the tool window
void MeshToCodeUtil::DestroyWindow()
{
HWND parent = GetParent(toolHWND);
if(parent)
{
GetCUIFrameMgr()->RecalcLayout(TRUE);
::DestroyWindow(parent);
}
toolHWND = NULL;
#ifndef DISABLE_FB_INTERFACE
MeshToCodeUtilObjFPInterface::SetWindowOpen(false);
#endif
}
//**************************************************************************************
class filefilter
{
tstr fstr;
public:
void append(tstr text, tstr ext)
{
fstr += text;
fstr.push_back('\0');
fstr += ext;
fstr.push_back('\0');
}
tstr& getfilter() { return fstr; }
};
//*****************************************************************************************
void MeshToCodeUtil::DoMeshToCode(BOOL assignSMG)
{
//mprintf("TBID_ASSIGN_SMG_CHK checked = %d \n",assignSMG);
int numSelNodes = ip->GetSelNodeCount();
HWND frameHWND = GetParent(toolHWND);
if(numSelNodes != 1)
{
MessageBox(frameHWND, _T("You must select an object with a mesh."), _T("Mesh To Cpp"), MB_OK);
return;
}
INode* theNode = ip->GetSelNode(0);
if(!CanConvertToTriMesh(theNode))
{
MessageBox(frameHWND, _T("You must select an object with a mesh."), _T("Mesh To Cpp"), MB_OK);
return;
}
tstr filename(theNode->GetName());
filename += _T(".cpp");
tstr initdir(ip->GetDir(APP_EXPORT_DIR));
tstr defext(_T("cpp"));
filefilter filter;
filter.append(_T("Source Files (*.cpp)"), _T("*.cpp"));
filter.append(_T("Include Files (*.h)"), _T("*.h"));
if(!DoOpenSaveDialog(frameHWND, initdir, filename, filter.getfilter(), defext, 1, false))
return;
FILE* fstream = _tfopen(filename.c_str(), _T("w"));
if(!fstream)
{
MessageBox(frameHWND, _T("Unable to open file for writing."), _T("Mesh To Cpp"), MB_OK);
return;
}
BOOL deleteIt = FALSE;
TriObject* meshObj = GetTriObject(theNode,ip->GetTime(),deleteIt);
if(!meshObj)
return;
cstr varname = widetochar(theNode->GetName());
Mesh& mesh = meshObj->GetMesh();
WriteMeshToCPPFile(fstream, varname, mesh, assignSMG);
if (deleteIt) meshObj->DeleteMe();
fclose(fstream);
}
//********************************************************************************************
#define VERTPREC 0.0001f
#define UVPREC 0.00001f
inline float round(float f,float prec)
{
float inv = 1.0f/prec;
return (float)(floor(f * inv + 0.5f)/inv);
}
inline Point3 roundPoint(Point3& p, float prec) { return Point3(round(p.x,prec), round(p.y,prec), round(p.z,prec)); }
//********************************************************************************************
void MeshToCodeUtil::WriteMeshToCPPFile(FILE* fstream, cstr& varname, Mesh& mesh, BOOL assignSMG)
{
int nverts = mesh.numVerts;
int nfaces = mesh.numFaces;
fprintf(fstream, "static void Build%sMesh(Mesh& mesh, float size)\n{\n\tmesh.setNumVerts(%d);\n\tmesh.setNumFaces(%d);\n\n",
varname.c_str(), nverts, nfaces);
// geometry verts
Point3* verts = mesh.verts; // pointer to the verts
for(int v = 0; v < nverts; ++v)
{
Point3 vert = roundPoint(verts[v], VERTPREC);
fprintf(fstream, "\tmesh.setVert(%d,size*Point3(%.4ff,%.4ff,%.4ff));\n", v, vert.x, vert.y, vert.z);
}
// faces
fprintf(fstream, "\n");
Face* faces = mesh.faces; // pointer to the faces
for(int f = 0; f < nfaces; ++f)
{
Face& face = faces[f];
fprintf(fstream, "\tmesh.faces[%d].setVerts(%d, %d, %d);\n", f, face.v[0], face.v[1], face.v[2]);
fprintf(fstream, "\tmesh.faces[%d].setEdgeVisFlags(%d,%d,%d);\n", f, face.getEdgeVis(0) ? 1 : 0,
face.getEdgeVis(1) ? 1 : 0, face.getEdgeVis(2)) ? 1 : 0;
if(assignSMG)
fprintf(fstream, "\tmesh.faces[%d].setSmGroup(%d);\n", f, face.getSmGroup());
}
// uvw coords
int ntverts = mesh.numTVerts;
if(ntverts > 0)
{
fprintf(fstream, "\n\tmesh.setNumTVerts(%d);\n\tmesh.setNumTVFaces(%d);\n\n", ntverts, nfaces);
Point3* tverts = mesh.tVerts;
for(int v = 0; v < ntverts; ++v)
{
Point3 tvert = roundPoint(tverts[v], UVPREC);
fprintf(fstream, "\tmesh.setTVert(%d,Point3(%.5ff,%.5ff,%.5ff));\n", v, tvert.x, tvert.y, tvert.z);
}
fprintf(fstream, "\n");
TVFace* tfaces = mesh.tvFace;
for(int f = 0; f < nfaces; ++f)
{
TVFace& tface = tfaces[f];
fprintf(fstream, "\tmesh.tvFace[%d].setTVerts(%d, %d, %d);\n", f,tface.t[0],tface.t[1],tface.t[2]);
}
}
// colour per vertex
int ncverts = mesh.numCVerts;
if(ncverts > 0)
{
fprintf(fstream, "\n\tmesh.setNumVertCol(%d);\n\tmesh.setNumVCFaces(%d);\n\n", ncverts, nfaces);
Point3* cverts = mesh.vertCol;
for(int v = 0; v < ncverts; ++v)
{
Point3 cvert = roundPoint(cverts[v], UVPREC);
fprintf(fstream, "\tmesh.setMapVert(0, %d,Point3(%.5ff,%.5ff,%.5ff));\n", v, cvert.x, cvert.y, cvert.z);
}
fprintf(fstream, "\n");
TVFace* cfaces = mesh.vcFace;
for(int f = 0; f < nfaces; ++f)
{
TVFace& cface = cfaces[f];
fprintf(fstream, "\tmesh.mapFaces(0)[%d].setTVerts(%d, %d, %d);\n", f,cface.t[0],cface.t[1],cface.t[2]);
}
}
// mapping channel 2
if(mesh.mapSupport(2))
{
int nmverts = mesh.getNumMapVerts(2);
fprintf(fstream, "\n\tmesh.setMapSupport(2);\n\tmesh.setNumMapVerts(2,%d);\n\tmesh.setNumMapFaces(2,%d);\n\n",
nmverts, nfaces);
Point3* mverts = mesh.mapVerts(2);
for(int v = 0; v < nmverts; ++v)
{
Point3 mvert = roundPoint(mverts[v], UVPREC);
fprintf(fstream, "\tmesh.setMapVert(2, %d,Point3(%.5ff,%.5ff,%.5ff));\n", v, mvert.x, mvert.y, mvert.z);
}
fprintf(fstream, "\n");
TVFace* mfaces = mesh.mapFaces(2);
for(int f = 0; f < nfaces; ++f)
{
TVFace& mface = mfaces[f];
fprintf(fstream, "\tmesh.mapFaces(2)[%d].setTVerts(%d, %d, %d);\n", f,mface.t[0],mface.t[1],mface.t[2]);
}
}
fprintf(fstream, "\n\tmesh.InvalidateGeomCache();\n\tmesh.EnableEdgeList(TRUE);\n}\n");
//MeshToCodeUtilOps.OpenWindow()
}
//********************************************************************************************
thank you KLVNK. I more or less understand what to do with custom tool bar. I am not clear about dockable dialogs.
Check the situation in the max … To make a Rollout dialog dockable we have to Register it. But look at Scene Explorer dialog. It’s registered already but stays in Dialog state (not Tool Window state). It keeps Minimize and Maximize ability, as well as can be docked. Why can I not set a Rollout Dialog in the same state?
I want to have my MAX Dialog behaves like a Track View, or Scene Explorer, or Layer Explorer windows… Be ‘minimizable, maximizable, dockable, floatable’ …
I couldn’t find anything about how to doing it with the SDK.
However, for .Net forms (like the Scene or Layer Explorer), there is a class WinFormsCUIFrame in the ManagedServices assembly with some methods to work with this.
(
form = dotnetobject "System.Windows.Forms.Form"
CUIFrame = dotnetobject "ManagedServices.WinFormsCUIFrame" form
)
Thanks, Jorge, once again. This is not what I need, but now I know for sure that this is possible.
It must be possible at least with native windows, like the Track View Editor. I don’t know about MXS rollouts. Perhaps they have some kind of limitation?
I’ve tried re-styling the registered Rollouts, but couldn’t get them to work.