cwUi : Initial UI creation from resource file.

This commit is contained in:
kevin.larke 2020-03-31 13:10:45 -04:00
parent 80cd776337
commit 2372f153b3
6 changed files with 725 additions and 191 deletions

View File

@ -1,6 +1,7 @@
# To Do # To Do
- implement floating point UI numbers
- UI needs a special UUID (not kInvalidId) to specify the 'root' UI element. See note in cwUi._createFromObj() - UI needs a special UUID (not kInvalidId) to specify the 'root' UI element. See note in cwUi._createFromObj()
- Look at 'BUG' warnings in cwNumericConvert.h. - Look at 'BUG' warnings in cwNumericConvert.h.
- cwObject must be able to parse without dynamic memory allocation into a fixed buffer - cwObject must be able to parse without dynamic memory allocation into a fixed buffer
@ -33,7 +34,24 @@ This is easy to reproduce by simply decreasing the size of the buffers in the pr
- (DONE) implement kTcpFl in cwTcpSocket.cpp - (DONE) implement kTcpFl in cwTcpSocket.cpp
# UI Control Creation Protocol
The UI elements have four identifiers:
uuId - An integer which is unique among all identifiers for a given cwUi object.
appId - A constant (enumerated) id assigned by the application. Unique among siblings.
jsId - A string id used by Javascript to identify a control. Unique among siblings.
jsUuId - An integer which is unique among all identifers for the browser representation of a given cwUi object.
The 'jsId' is selected by the application when the object is created.
The 'jsUuId' is generated by the JS client when the UI element is created.
The 'uuId' is generated by the UI server when the JS client registers the control.
The 'appId' is assigned by the UI server when the JS client regsiters the control.
Client sends 'init' message.
Server sends 'create' messages.
Client sends 'register' messages.
Server send' 'id_assign' messages.
# Development Setup # Development Setup

540
cwUi.cpp
View File

@ -14,6 +14,15 @@ namespace cw
{ {
namespace ui namespace ui
{ {
typedef struct appIdMapRecd_str
{
struct appIdMapRecd_str* link;
unsigned parentAppId;
unsigned appId;
char* jsId;
} appIdMapRecd_t;
typedef struct ele_str typedef struct ele_str
{ {
struct ele_str* parent; // pointer to parent ele - or nullptr if this ele is attached to the root ui ele struct ele_str* parent; // pointer to parent ele - or nullptr if this ele is attached to the root ui ele
@ -24,17 +33,29 @@ namespace cw
typedef struct ui_str typedef struct ui_str
{ {
websockSrv::handle_t wssH; websockSrv::handle_t wssH; // websock server handle
unsigned eleAllocN; unsigned eleAllocN; // size of eleA[]
unsigned eleN; unsigned eleN; // count of ele's in use
ele_t** eleA; ele_t** eleA; // eleA[ eleAllocN ]
uiCallback_t cbFunc; uiCallback_t cbFunc; // app. cb func
void* cbArg; void* cbArg; // app. cb func arg.
appIdMapRecd_t* appIdMap; // map of application parent/child/js id's
char* buf; // buf[bufN] output message formatting buffer
unsigned bufN; //
} ui_t; } ui_t;
ui_t* _handleToPtr( handle_t h ) ui_t* _handleToPtr( handle_t h )
{ return handleToPtr<handle_t,ui_t>(h); } { return handleToPtr<handle_t,ui_t>(h); }
void _print_eles( ui_t* p )
{
for(unsigned i=0; i<p->eleN; ++i)
{
ele_t* e = p->eleA[i];
printf("%15s u:%i : u:%i a:%i %s\n",e->parent==nullptr?"<null>" : e->parent->jsId,e->parent==nullptr? -1 :e->parent->uuId,e->uuId,e->appId,e->jsId);
}
}
rc_t _destroy( ui_t* p ) rc_t _destroy( ui_t* p )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
@ -49,19 +70,74 @@ namespace cw
mem::release(p->eleA[i]); mem::release(p->eleA[i]);
} }
appIdMapRecd_t* m = p->appIdMap;
while( m!=nullptr )
{
appIdMapRecd_t* m0 = m->link;
mem::release(m->jsId);
mem::release(m);
m = m0;
}
mem::release(p->eleA); mem::release(p->eleA);
mem::release(p); mem::release(p);
return rc; return rc;
} }
appIdMapRecd_t* _findAppIdMap( ui_t* p, unsigned parentAppId, const char* jsId )
{
appIdMapRecd_t* m = p->appIdMap;
for(; m != nullptr; m=m->link)
if( m->parentAppId==parentAppId && textCompare(jsId,m->jsId)==0 )
return m;
return nullptr;
}
appIdMapRecd_t* _findAppIdMap( ui_t* p, unsigned parentAppId, unsigned appId )
{
appIdMapRecd_t* m = p->appIdMap;
for(; m != nullptr; m=m->link)
if( m->parentAppId==parentAppId && m->appId==appId )
return m;
return nullptr;
}
rc_t _allocAppIdMap( ui_t* p, unsigned parentAppId, unsigned appId, const char* jsId )
{
rc_t rc = kOkRC;
// The 'jsId' must be valid (or there is no reason to create the map.
// (since it will ultimately be used to locate the appId give the parentAppId and jsId)
if( jsId == nullptr || strlen(jsId) == 0 )
return cwLogError(kInvalidIdRC,"Registered parent/child app id's must have a valid 'jsId'.");
// verify that the parent/child pair is unique
if( _findAppIdMap(p,parentAppId,appId) != nullptr )
return cwLogError(kDuplicateRC,"An attempt was made to register a duplicate parent/child appid pair. parentId:%i appId:%i jsId:'%s'.",parentAppId,appId,cwStringNullGuard(jsId));
// verify that the parent/js pair is unique
if( _findAppIdMap(p,parentAppId,jsId) != nullptr )
return cwLogError(kDuplicateRC,"An attempt was made to register a duplicate parent app id/js id pair. parentId:%i appId:%i jsId:'%s'.",parentAppId,appId,cwStringNullGuard(jsId));
// allocate and link in a new appId map record
appIdMapRecd_t* m = mem::allocZ<appIdMapRecd_t>();
m->parentAppId = parentAppId;
m->appId = appId;
m->jsId = mem::duplStr(jsId);
m->link = p->appIdMap;
p->appIdMap = m;
return rc;
}
ele_t* _createEle( ui_t* p, ele_t* parent, unsigned appId, const char* jsId ) ele_t* _createEle( ui_t* p, ele_t* parent, unsigned appId, const char* jsId )
{ {
ele_t* e = mem::allocZ<ele_t>(); ele_t* e = mem::allocZ<ele_t>();
e->parent = parent; e->parent = parent;
e->uuId = p->eleN; e->uuId = p->eleN;
e->appId = appId; e->appId = appId;
e->jsId = mem::duplStr(jsId); e->jsId = mem::duplStr(jsId);
if( p->eleN == p->eleAllocN ) if( p->eleN == p->eleAllocN )
{ {
@ -75,6 +151,7 @@ namespace cw
return e; return e;
} }
// Given a uuId return a pointer to the associated element.
ele_t* _uuIdToEle( ui_t* p, unsigned uuId, bool errorFl=true ) ele_t* _uuIdToEle( ui_t* p, unsigned uuId, bool errorFl=true )
{ {
if( uuId >= p->eleN ) if( uuId >= p->eleN )
@ -86,10 +163,26 @@ namespace cw
return p->eleA[ uuId ]; return p->eleA[ uuId ];
} }
// Given a parent UuId and a javascript id find the associated ele
rc_t _websockSend( ui_t* p, const char* msg ) ele_t* _parentUuId_JsId_ToEle( ui_t* p, unsigned parentUuId, const char* jsId, bool errorFl=true )
{ {
return websock::send( websockSrv::websockHandle( p->wssH ), kUiProtocolId, msg, strlen(msg) ); for(unsigned i=0; i<p->eleN; ++i)
if( ((p->eleA[i]->parent==nullptr && parentUuId == kRootUuId) || (p->eleA[i]->parent != nullptr && parentUuId == p->eleA[i]->parent->uuId)) && (strcmp(p->eleA[i]->jsId,jsId) == 0))
return p->eleA[i];
if( errorFl )
cwLogError(kInvalidIdRC,"The element with parent uuid:%i and jsId:%s is not found.",parentUuId,jsId);
return nullptr;
}
unsigned _findElementUuId( ui_t* p, const char* jsId )
{
for(unsigned i=0; i<p->eleN; ++i)
if( strcmp(p->eleA[i]->jsId,jsId) == 0 )
return p->eleA[i]->uuId;
return kInvalidId;
} }
const char* _findEleJsId( ui_t* p, unsigned uuId ) const char* _findEleJsId( ui_t* p, unsigned uuId )
@ -101,6 +194,12 @@ namespace cw
return nullptr; return nullptr;
} }
rc_t _websockSend( ui_t* p, unsigned wsSessId, const char* msg )
{
return websock::send( websockSrv::websockHandle( p->wssH ), kUiProtocolId, wsSessId, msg, strlen(msg) );
}
// Print the attribute data value. // Print the attribute data value.
template< typename T0 > template< typename T0 >
unsigned format_attribute_data( char* buf, unsigned n, T0 t0 ) unsigned format_attribute_data( char* buf, unsigned n, T0 t0 )
@ -135,69 +234,137 @@ namespace cw
} }
template< typename... ARGS> template< typename... ARGS>
rc_t _createOneEle( ui_t* p, unsigned& uuIdRef, const char* eleTypeStr, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, ARGS&&... args ) rc_t _createOneEle( ui_t* p, unsigned& uuIdRef, const char* eleTypeStr, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, ARGS&&... args )
{ {
// { op:create, parent:my_parent_id, value:{ button:{ jsId:my_jsId, appId:appId, uuId:uuId, class:clas, title:'my title' } } // { op:create, parent:my_parent_id, value:{ button:{ jsId:my_jsId, appId:appId, uuId:uuId, class:clas, title:'my title' } }
rc_t rc = kOkRC; rc_t rc = kOkRC;
const char* parentJsId = ""; const char* parentJsId = "";
ele_t* newEle = nullptr; ele_t* newEle = nullptr;
ele_t* parentEle = nullptr; ele_t* parentEle = nullptr;
const unsigned bufN = 1024; //const unsigned bufN = 1024; // TODO: use preallocated buffer
char buf[ bufN ]; //char buf[ bufN ];
uuIdRef = kInvalidId; uuIdRef = kInvalidId;
// if( parentUuId == kInvalidId )
if( parentUuId != kInvalidId ) parentUuId = kRootUuId;
{
if(( parentEle = _uuIdToEle(p, parentUuId )) == nullptr )
return cwLogError( kInvalidArgRC, "Unable to locate the parent element (id:%i).", parentUuId );
parentJsId = parentEle->jsId; // get the parent element
} if(( parentEle = _uuIdToEle(p, parentUuId )) == nullptr )
return cwLogError( kInvalidArgRC, "Unable to locate the parent element (id:%i).", parentUuId );
// get the parent jsId
parentJsId = parentEle->jsId;
// create the local representation of the new element
newEle = _createEle( p, parentEle, appId, jsId ); newEle = _createEle( p, parentEle, appId, jsId );
unsigned i = snprintf( buf, bufN, "{ \"op\":\"create\", \"parent\":\"%s\", \"children\":{ \"%s\":{ \"jsId\":\"%s\", \"appId\":%i, \"uuId\":%i, \"class\":\"%s\", \"title\":\"%s\" ", parentJsId, eleTypeStr, jsId, appId, newEle->uuId, clas, title ); // form the create json message string
unsigned i = snprintf( p->buf, p->bufN, "{ \"op\":\"create\", \"parent\":\"%s\", \"children\":{ \"%s\":{ \"jsId\":\"%s\", \"appId\":%i, \"uuId\":%i, \"class\":\"%s\", \"title\":\"%s\" ", parentJsId, eleTypeStr, jsId, appId, newEle->uuId, clas, title );
i = format_attributes(buf, bufN, i, std::forward<ARGS>(args)...); // add the UI specific attributes
i += format_attributes(p->buf+i, p->bufN-i, 0, std::forward<ARGS>(args)...);
toText(buf+i, bufN-i, "}}}"); // terminate the message
i += toText(p->buf+i, p->bufN-i, "}}}");
printf("%s\n",buf); if( i >= p->bufN )
return cwLogError(kBufTooSmallRC,"The UI message formatting buffer is too small. (size:%i bytes)", p->bufN);
rc = _websockSend( p, buf ); printf("%s\n",p->buf);
// send the message
rc = _websockSend( p, wsSessId, p->buf );
uuIdRef = newEle->uuId; uuIdRef = newEle->uuId;
return rc; return rc;
} }
rc_t _decorateObj( ui_t* p, object_t* o )
rc_t _createFromObj( ui_t* p, object_t* o, unsigned parentUuId )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
object_t* pid; const object_t* oo;
ele_t* parent_ele;
const char* jsId;
// BUG BUG BUG - if kInvalidId is used both to indicate a NULL valid // find the parent pair
// and the root object then there is no way to override the parent if((oo = o->find( "parent", kNoRecurseFl | kOptionalFl)) != nullptr )
// that is coded into the file with the root object. A special
// rootId needs to be created.
if((pid = o->find("parent",false)) != nullptr && parentUuId == kInvalidId )
{ {
// get the parent JS id from the cfg object
// get the parent uuid from the JS id (verify that there is no ambiguity)
// delete the 'parent' pair
} }
// form the msg string // find the parent JsId
if((rc = oo->value(jsId)) != kOkRC )
{
}
// find the parent element
//if((parent_ele = _jsIdToEle( p, jsId )) == nullptr )
//{
//}
}
rc_t _createFromObj( ui_t* p, object_t* o, unsigned wsSessId, unsigned parentUuId )
{
rc_t rc = kOkRC;
const char* parentJsId = "";
const int kBufN = 512; // TODO: preallocate this buffer as part of ui_t.
char buf0[ kBufN ];
char buf1[ kBufN ];
// if a parentUuid was given ...
if( parentUuId != kInvalidId )
{
// ... then find the associated JS id
if((parentJsId = _findEleJsId( p, parentUuId )) == nullptr )
return cwLogError(kInvalidIdRC, "The JS id associated with the uuid '%i' could not be found for an resource object.", parentUuId );
}
else // if no parentUuid was given then look for one in the resource
{
// get the parent JS id from the cfg object
rc = o->get("parent",parentJsId,kNoRecurseFl | kOptionalFl);
switch(rc)
{
case kOkRC:
// get a pointer to the jsId from the local list (the copy in the object is about to be deleted)
parentJsId = _findEleJsId( p, _findElementUuId(p,parentJsId));
//remove the parent link
o->find("parent")->parent->free();
break;
case kLabelNotFoundRC:
parentJsId = _findEleJsId( p, kRootUuId );
break;
default:
rc = cwLogError(rc,"The resource object parent id '%s' could not be found.", parentJsId );
goto errLabel;
}
}
// form the msg string from the resource
if( o->to_string( buf0, kBufN ) >= kBufN )
return cwLogError(kBufTooSmallRC,"The resource object string buffer is too small (buf bytes:%i).",kBufN);
printf("buf0: %s\n",buf0);
if( snprintf( buf1, kBufN, "{ \"op\":\"create\", \"parent\":\"%s\", \"children\":%s }", parentJsId, buf0 ) >= kBufN )
return cwLogError(kBufTooSmallRC,"The resource object string buffer is too small (buf bytes:%i).",kBufN);
// send the msg string // send the msg string
printf("buf1: %s\n",buf1);
rc = _websockSend( p, wsSessId, buf1 );
errLabel:
return rc; return rc;
} }
@ -221,7 +388,7 @@ namespace cw
if( s == nullptr || sscanf(msg, "value %i %c ",&eleUuId,&argType) != 2 ) if( s == nullptr || sscanf(msg, "value %i %c ",&eleUuId,&argType) != 2 )
{ {
cwLogError(kSyntaxErrorRC,"Invalid message from UI: %s.", msg ); cwLogError(kSyntaxErrorRC,"Invalid message from UI: '%s'.", msg );
goto errLabel; goto errLabel;
} }
@ -278,13 +445,122 @@ namespace cw
return ele; return ele;
} }
void _websockCb( void* cbArg, unsigned protocolId, unsigned connectionId, websock::msgTypeId_t msg_type, const void* msg, unsigned byteN ) rc_t _send_app_id_msg( ui_t* p, unsigned wsSessId, ele_t* ele )
{
rc_t rc = kOkRC;
unsigned i = snprintf(p->buf,p->bufN,"{ \"op\":\"set_app_id\", \"parentUuId\":%i, \"jsId\":\"%s\", \"appId\":%i, \"uuId\":%i }", ele->parent->uuId, ele->jsId, ele->parent->appId, ele->appId );
if( i >= p->bufN )
return cwLogError(kBufTooSmallRC,"The 'app_id' msg formatting buffer is too small (%i bytes).", p->bufN);
if((rc = _websockSend( p, wsSessId, p->buf )) != kOkRC )
return cwLogError(rc,"'app_id' msg transmission failed.");
return rc;
}
ele_t* _handle_register_msg( ui_t* p, unsigned wsSessId, const char* msg )
{
printf("%s\n",msg);
return nullptr;
}
ele_t* _handle_register_msg0( ui_t* p, unsigned wsSessId, const char* msg )
{
rc_t rc = kOkRC;
unsigned parentUuId = kInvalidId;
ele_t* parentEle = nullptr;
ele_t* ele = nullptr;
const char* s0 = nextNonWhiteChar(msg + strlen("register"));
const char* jsId = nextNonWhiteChar(nextWhiteChar(s0));
// verifity the message tokens
if( s0 == nullptr || jsId == nullptr )
{
cwLogError(kSyntaxErrorRC, "'register' msg format error: '%s' is not a valid message.", cwStringNullGuard(msg) );
goto errLabel;
}
// verify the parentUuId parsing
if((rc = string_to_number<unsigned>(s0,parentUuId)) != kOkRC )
{
cwLogError(kSyntaxErrorRC, "'register' msg parentUuId format error: '%s' does not contain a valid parentUuId.", cwStringNullGuard(msg) );
goto errLabel;
}
// get the parent ele
if((parentEle = _uuIdToEle( p, parentUuId)) == nullptr )
{
cwLogError(kInvalidIdRC,"UI register msg parent element not found.");
goto errLabel;
}
// if the child element does not already exist
if(( ele = _parentUuId_JsId_ToEle( p, parentUuId, jsId, false )) == nullptr )
{
// look up the parent/jsId pair map
appIdMapRecd_t* m = _findAppIdMap( p, parentEle->appId, jsId );
// create the ele
ele = _createEle( p, parentEle, m==nullptr ? kInvalidId : m->appId, jsId );
printf("creating: parent uuid:%i js:%s \n", parentUuId,jsId);
// notify the app of the new ele's uuid and appId
if( m != nullptr )
_send_app_id_msg( p, wsSessId, ele );
}
else
{
printf("parent uuid:%i js:%s already exists.\n", parentUuId,jsId);
}
if( ele != nullptr )
_send_app_id_msg( p, wsSessId, ele );
return ele;
errLabel:
return nullptr;
}
opId_t _labelToOpId( const char* label )
{
typedef struct
{
opId_t id;
const char* label;
} map_t;
map_t mapA[] =
{
{ kConnectOpId, "connect" },
{ kInitOpId, "init" },
{ kValueOpId, "value" },
{ kRegisterOpId, "register" },
{ kDisconnectOpId, "disconnect" },
{ kEndAppIdUpdateOpId, "end_app_id_update" },
{ kInvalidOpId, "<invalid>" },
};
for(unsigned i=0; mapA[i].id != kInvalidOpId; ++i)
if( textCompare(label,mapA[i].label,strlen(mapA[i].label)) == 0 )
return mapA[i].id;
return kInvalidOpId;
}
void _websockCb( void* cbArg, unsigned protocolId, unsigned wsSessId, websock::msgTypeId_t msg_type, const void* msg, unsigned byteN )
{ {
ui_t* p = (ui_t*)cbArg; ui_t* p = (ui_t*)cbArg;
opId_t opId = kInvalidOpId; opId_t opId = kInvalidOpId;
unsigned eleUuId = kInvalidId;
unsigned eleAppId = kInvalidId;
unsigned parentEleAppId = kInvalidId;
value_t value; value_t value;
switch( msg_type ) switch( msg_type )
@ -301,23 +577,54 @@ namespace cw
{ {
ele_t* ele; ele_t* ele;
if( textCompare((const char*)msg,"init",strlen("init")) == 0 ) opId = _labelToOpId((const char*)msg);
switch( opId )
{ {
opId = kInitOpId; case kInitOpId:
} // Pass on the 'init' msg to the app.
else p->cbFunc( p->cbArg, wsSessId, opId, kInvalidId, kInvalidId, kInvalidId, nullptr );
{
opId = kValueOpId; // The UI is initialized - begin the id update process
if((ele = _parse_value_msg(p, value, (const char*)msg )) == nullptr ) if( _websockSend( p, wsSessId, "{ \"op\":\"begin_app_id_update\" }" ) != kOkRC )
cwLogError(kOpFailRC,"UI Value message parse failed."); cwLogError(kOpFailRC,"'begin_app_id_update' transmit failed.");
else
{ break;
eleUuId = ele->uuId;
eleAppId = ele->appId; case kValueOpId:
parentEleAppId = ele->parent == nullptr ? kInvalidId : ele->parent->appId; if((ele = _parse_value_msg(p, value, (const char*)msg )) == nullptr )
} cwLogError(kOpFailRC,"UI Value message parse failed.");
} else
} {
unsigned parentEleAppId = ele->parent == nullptr ? kInvalidId : ele->parent->appId;
p->cbFunc( p->cbArg, wsSessId, opId, parentEleAppId, ele->uuId, ele->appId, &value );
}
break;
case kRegisterOpId:
_handle_register_msg(p, wsSessId, (const char*)msg );
break;
case kEndAppIdUpdateOpId:
_print_eles( p );
cwLogInfo("App Id Update Complete.");
break;
case kInvalidOpId:
cwLogError(kInvalidIdRC,"The UI received a NULL op. id.");
break;
default:
cwLogError(kInvalidIdRC,"The UI received an unknown op. id.");
break;
} // switch opId
} // kMessageTId
break; break;
default: default:
@ -325,8 +632,8 @@ namespace cw
return; return;
} }
if( p->cbFunc != nullptr )
p->cbFunc( p->cbArg, connectionId, opId, parentEleAppId, eleUuId, eleAppId, &value );
} }
} }
} }
@ -340,9 +647,11 @@ cw::rc_t cw::ui::createUi(
const char* dfltPageFn, const char* dfltPageFn,
unsigned websockTimeOutMs, unsigned websockTimeOutMs,
unsigned rcvBufByteN, unsigned rcvBufByteN,
unsigned xmtBufByteN ) unsigned xmtBufByteN,
unsigned fmtBufByteN)
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
ele_t* ele;
websock::protocol_t protocolA[] = websock::protocol_t protocolA[] =
{ {
@ -355,6 +664,9 @@ cw::rc_t cw::ui::createUi(
if((rc = destroyUi(h)) != kOkRC ) if((rc = destroyUi(h)) != kOkRC )
return rc; return rc;
if( cbFunc == nullptr )
return cwLogError(kInvalidArgRC,"The UI callback function must be a valid pointer.");
ui_t* p = mem::allocZ<ui_t>(); ui_t* p = mem::allocZ<ui_t>();
if((rc = websockSrv::create(p->wssH, _websockCb, p, physRootDir, dfltPageFn, port, protocolA, protocolN, websockTimeOutMs )) != kOkRC ) if((rc = websockSrv::create(p->wssH, _websockCb, p, physRootDir, dfltPageFn, port, protocolA, protocolN, websockTimeOutMs )) != kOkRC )
@ -368,6 +680,15 @@ cw::rc_t cw::ui::createUi(
p->eleN = 0; p->eleN = 0;
p->cbFunc = cbFunc; p->cbFunc = cbFunc;
p->cbArg = cbArg; p->cbArg = cbArg;
p->buf = mem::allocZ<char>(fmtBufByteN);
p->bufN = fmtBufByteN;
// create the root element
if((ele = _createEle(p, nullptr, kRootEleAppId, "uiDivId" )) == nullptr || ele->uuId != kRootUuId )
{
cwLogError(kOpFailRC,"The UI root element creation failed.");
goto errLabel;
}
h.set(p); h.set(p);
@ -430,11 +751,12 @@ unsigned cw::ui::findElementAppId( handle_t h, unsigned parentUuId, const char*
unsigned cw::ui::findElementUuId( handle_t h, unsigned parentUuId, const char* jsId ) unsigned cw::ui::findElementUuId( handle_t h, unsigned parentUuId, const char* jsId )
{ {
ui_t* p = _handleToPtr(h); ui_t* p = _handleToPtr(h);
ele_t* ele;
if((ele = _parentUuId_JsId_ToEle(p, parentUuId, jsId )) != nullptr )
return ele->uuId;
for(unsigned i=0; i<p->eleN; ++i)
if( p->eleA[i]->parent->uuId==parentUuId && strcmp(p->eleA[i]->jsId,jsId) == 0 )
return p->eleA[i]->uuId;
return kInvalidId; return kInvalidId;
} }
@ -454,8 +776,15 @@ const char* cw::ui::findElementJsId( handle_t h, unsigned uuId )
return _findEleJsId(p,uuId); return _findEleJsId(p,uuId);
} }
unsigned cw::ui::findElementUuId( handle_t h, const char* jsId )
{
ui_t* p = _handleToPtr(h);
cw::rc_t cw::ui::createFromFile( handle_t h, const char* fn, unsigned parentUuId) return _findElementUuId(p,jsId);
}
cw::rc_t cw::ui::createFromFile( handle_t h, const char* fn, unsigned wsSessId, unsigned parentUuId)
{ {
ui_t* p = _handleToPtr(h); ui_t* p = _handleToPtr(h);
rc_t rc = kOkRC; rc_t rc = kOkRC;
@ -464,12 +793,14 @@ cw::rc_t cw::ui::createFromFile( handle_t h, const char* fn, unsigned parent
if((rc = objectFromFile( fn, o )) != kOkRC ) if((rc = objectFromFile( fn, o )) != kOkRC )
goto errLabel; goto errLabel;
if((rc = _createFromObj( p, o, parentUuId )) != kOkRC ) //o->print();
if((rc = _createFromObj( p, o, wsSessId, parentUuId )) != kOkRC )
goto errLabel; goto errLabel;
errLabel: errLabel:
if(rc != kOkRC ) if(rc != kOkRC )
rc = cwLogError(rc,"UI from configuration the file '%s' failed.", cwStringNullGuard(fn)); rc = cwLogError(rc,"UI instantiation from the configuration file '%s' failed.", cwStringNullGuard(fn));
if( o != nullptr ) if( o != nullptr )
o->free(); o->free();
@ -477,7 +808,7 @@ cw::rc_t cw::ui::createFromFile( handle_t h, const char* fn, unsigned parent
return rc; return rc;
} }
cw::rc_t cw::ui::createFromText( handle_t h, const char* text, unsigned parentUuId) cw::rc_t cw::ui::createFromText( handle_t h, const char* text, unsigned wsSessId, unsigned parentUuId)
{ {
ui_t* p = _handleToPtr(h); ui_t* p = _handleToPtr(h);
rc_t rc = kOkRC; rc_t rc = kOkRC;
@ -486,12 +817,12 @@ cw::rc_t cw::ui::createFromText( handle_t h, const char* text, unsigned parentU
if((rc = objectFromString( text, o )) != kOkRC ) if((rc = objectFromString( text, o )) != kOkRC )
goto errLabel; goto errLabel;
if((rc = _createFromObj( p, o, parentUuId )) != kOkRC ) if((rc = _createFromObj( p, o, wsSessId, parentUuId )) != kOkRC )
goto errLabel; goto errLabel;
errLabel: errLabel:
if(rc != kOkRC ) if(rc != kOkRC )
rc = cwLogError(rc,"UI from configuration the string '%s' failed.", cwStringNullGuard(text)); rc = cwLogError(rc,"UI instantiation failed from the configuration from string: '%s'.", cwStringNullGuard(text));
if( o != nullptr ) if( o != nullptr )
o->free(); o->free();
@ -499,34 +830,34 @@ cw::rc_t cw::ui::createFromText( handle_t h, const char* text, unsigned parentU
return rc; return rc;
} }
cw::rc_t cw::ui::createDiv( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title ) cw::rc_t cw::ui::createDiv( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title )
{ return _createOneEle( _handleToPtr(h), uuIdRef, "div", parentUuId, jsId, appId, clas, title ); } { return _createOneEle( _handleToPtr(h), uuIdRef, "div", wsSessId, parentUuId, jsId, appId, clas, title ); }
cw::rc_t cw::ui::createTitle( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title ) cw::rc_t cw::ui::createTitle( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title )
{ return _createOneEle( _handleToPtr(h), uuIdRef, "option", parentUuId, jsId, appId, clas, title ); } { return _createOneEle( _handleToPtr(h), uuIdRef, "option", wsSessId, parentUuId, jsId, appId, clas, title ); }
cw::rc_t cw::ui::createButton( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title ) cw::rc_t cw::ui::createButton( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title )
{ return _createOneEle( _handleToPtr(h), uuIdRef, "button", parentUuId, jsId, appId, clas, title ); } { return _createOneEle( _handleToPtr(h), uuIdRef, "button", wsSessId, parentUuId, jsId, appId, clas, title ); }
cw::rc_t cw::ui::createCheck( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, bool value ) cw::rc_t cw::ui::createCheck( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, bool value )
{ return _createOneEle( _handleToPtr(h), uuIdRef, "check", parentUuId, jsId, appId, clas, title, "value", value ); } { return _createOneEle( _handleToPtr(h), uuIdRef, "check", wsSessId, parentUuId, jsId, appId, clas, title, "value", value ); }
cw::rc_t cw::ui::createSelect( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title ) cw::rc_t cw::ui::createSelect( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title )
{ return _createOneEle( _handleToPtr(h), uuIdRef, "select", parentUuId, jsId, appId, clas, title ); } { return _createOneEle( _handleToPtr(h), uuIdRef, "select", wsSessId, parentUuId, jsId, appId, clas, title ); }
cw::rc_t cw::ui::createOption( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title ) cw::rc_t cw::ui::createOption( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title )
{ return _createOneEle( _handleToPtr(h), uuIdRef, "option", parentUuId, jsId, appId, clas, title ); } { return _createOneEle( _handleToPtr(h), uuIdRef, "option", wsSessId, parentUuId, jsId, appId, clas, title ); }
cw::rc_t cw::ui::createString( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, const char* value ) cw::rc_t cw::ui::createString( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, const char* value )
{ return _createOneEle( _handleToPtr(h), uuIdRef, "string", parentUuId, jsId, appId, clas, title, "value", value ); } { return _createOneEle( _handleToPtr(h), uuIdRef, "string", wsSessId, parentUuId, jsId, appId, clas, title, "value", value ); }
cw::rc_t cw::ui::createNumber( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, double value, double minValue, double maxValue, double stepValue, unsigned decpl ) cw::rc_t cw::ui::createNumber( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, double value, double minValue, double maxValue, double stepValue, unsigned decpl )
{ return _createOneEle( _handleToPtr(h), uuIdRef, "number", parentUuId, jsId, appId, clas, title, "value", value, "min", minValue, "max", maxValue, "step", stepValue, "decpl", decpl ); } { return _createOneEle( _handleToPtr(h), uuIdRef, "number", wsSessId, parentUuId, jsId, appId, clas, title, "value", value, "min", minValue, "max", maxValue, "step", stepValue, "decpl", decpl ); }
cw::rc_t cw::ui::createProgress( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, double value, double minValue, double maxValue ) cw::rc_t cw::ui::createProgress( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, double value, double minValue, double maxValue )
{ return _createOneEle( _handleToPtr(h), uuIdRef, "progress", parentUuId, jsId, appId, clas, title, "value", value, "min", minValue, "max", maxValue ); } { return _createOneEle( _handleToPtr(h), uuIdRef, "progress", wsSessId, parentUuId, jsId, appId, clas, title, "value", value, "min", minValue, "max", maxValue ); }
cw::rc_t cw::ui::createText( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title ) cw::rc_t cw::ui::createText( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title )
{ {
rc_t rc= kOkRC; rc_t rc= kOkRC;
return rc; return rc;
@ -534,4 +865,19 @@ cw::rc_t cw::ui::createText( handle_t h, unsigned& uuIdRef, unsigned parentUuI
cw::rc_t cw::ui::registerAppIds( handle_t h, const appIdMap_t* map, unsigned mapN )
{
ui_t* p = _handleToPtr(h);
rc_t rc = kOkRC;
for(unsigned i=0; i<mapN; ++i)
if((rc = _allocAppIdMap( p, map[i].parentAppId, map[i].appId, map[i].jsId )) != kOkRC )
return rc;
return rc;
}

53
cwUi.h
View File

@ -21,6 +21,8 @@ namespace cw
kConnectOpId, kConnectOpId,
kInitOpId, kInitOpId,
kValueOpId, kValueOpId,
kRegisterOpId,
kEndAppIdUpdateOpId,
kDisconnectOpId kDisconnectOpId
} opId_t; } opId_t;
@ -35,6 +37,13 @@ namespace cw
kStringTId kStringTId
} dtypeId_t; } dtypeId_t;
enum
{
kRootUuId = 0,
kRootEleAppId,
};
typedef struct typedef struct
{ {
dtypeId_t tid; dtypeId_t tid;
@ -49,7 +58,7 @@ namespace cw
} u; } u;
} value_t; } value_t;
typedef rc_t (*uiCallback_t)( void* cbArg, unsigned connId, opId_t opId, unsigned parentAppId, unsigned uuId, unsigned appId, const value_t* value ); typedef rc_t (*uiCallback_t)( void* cbArg, unsigned websockSessionId, opId_t opId, unsigned parentAppId, unsigned uuId, unsigned appId, const value_t* value );
rc_t createUi( handle_t& h, rc_t createUi( handle_t& h,
unsigned port, unsigned port,
@ -59,30 +68,44 @@ namespace cw
const char* dfltPageFn = "index.html", const char* dfltPageFn = "index.html",
unsigned websockTimeOutMs = 50, unsigned websockTimeOutMs = 50,
unsigned rcvBufByteN = 1024, unsigned rcvBufByteN = 1024,
unsigned xmtBufByteN = 1024); unsigned xmtBufByteN = 1024,
unsigned fmtBufByteN = 4096 );
rc_t destroyUi( handle_t& h ); rc_t destroyUi( handle_t& h );
rc_t start( handle_t h ); rc_t start( handle_t h );
rc_t stop( handle_t h ); rc_t stop( handle_t h );
unsigned findElementAppId( handle_t h, unsigned parentUuId, const char* jsId ); unsigned findElementAppId( handle_t h, unsigned parentUuId, const char* jsId );
unsigned findElementUuId( handle_t h, unsigned parentUuId, const char* jsId ); unsigned findElementUuId( handle_t h, unsigned parentUuId, const char* jsId );
unsigned findElementUuId( handle_t h, unsigned parentUuId, unsigned appId ); unsigned findElementUuId( handle_t h, unsigned parentUuId, unsigned appId );
const char* findElementJsId( handle_t h, unsigned uuId ); const char* findElementJsId( handle_t h, unsigned uuId );
rc_t createFromFile( handle_t h, const char* fn, unsigned parentUuId=kInvalidId); // Return the uuid of the first matching 'jsId'.
rc_t createFromText( handle_t h, const char* text, unsigned parentUuId=kInvalidId); unsigned findElementUuId( handle_t h, const char* jsId );
rc_t createDiv( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title );
rc_t createTitle( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title ); rc_t createFromFile( handle_t h, const char* fn, unsigned wsSessId, unsigned parentUuId=kInvalidId);
rc_t createButton( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title ); rc_t createFromText( handle_t h, const char* text, unsigned wsSessId, unsigned parentUuId=kInvalidId);
rc_t createCheck( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, bool value ); rc_t createDiv( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title );
rc_t createSelect( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title ); rc_t createTitle( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title );
rc_t createOption( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title ); rc_t createButton( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title );
rc_t createString( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, const char* value ); rc_t createCheck( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, bool value );
rc_t createNumber( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, double value, double minValue, double maxValue, double stepValue, unsigned decPl ); rc_t createSelect( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title );
rc_t createProgress( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, double value, double minValue, double maxValue ); rc_t createOption( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title );
rc_t createText( handle_t h, unsigned& uuIdRef, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title ); rc_t createString( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, const char* value );
rc_t createNumber( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, double value, double minValue, double maxValue, double stepValue, unsigned decPl );
rc_t createProgress( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title, double value, double minValue, double maxValue );
rc_t createText( handle_t h, unsigned& uuIdRef, unsigned wsSessId, unsigned parentUuId, const char* jsId, unsigned appId, const char* clas, const char* title );
typedef struct appIdMap_str
{
unsigned parentAppId;
unsigned appId;
const char* jsId;
} appIdMap_t;
// Register parent/child/js app id's
rc_t registerAppIds( handle_t h, const appIdMap_t* map, unsigned mapN );
} }
} }

View File

@ -12,7 +12,8 @@ namespace cw
{ {
typedef struct ui_test_str typedef struct ui_test_str
{ {
handle_t uiH; handle_t uiH;
const char* uiCfgFn;
} ui_test_t; } ui_test_t;
enum enum
@ -30,49 +31,51 @@ namespace cw
kProgressId kProgressId
}; };
rc_t _uiTestCreateUi( ui_test_t* p, unsigned connId ) rc_t _uiTestCreateUi( ui_test_t* p, unsigned wsSessId )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
unsigned uuid = kInvalidId; unsigned uuid = kInvalidId;
unsigned selUuId = kInvalidId; unsigned selUuId = kInvalidId;
unsigned divUuId = kInvalidId; unsigned divUuId = kInvalidId;
if((rc = createDiv( p->uiH, divUuId, kInvalidId, "myDivId", kDivId, "divClass", "My Panel" )) != kOkRC ) if((rc = createDiv( p->uiH, divUuId, wsSessId, kInvalidId, "myDivId", kDivId, "divClass", "My Panel" )) != kOkRC )
goto errLabel; goto errLabel;
if((rc = createButton( p->uiH, uuid, divUuId, "myBtnId", kBtnId, "btnClass", "Push Me" )) != kOkRC ) if((rc = createButton( p->uiH, uuid, wsSessId, divUuId, "myBtnId", kBtnId, "btnClass", "Push Me" )) != kOkRC )
goto errLabel; goto errLabel;
if((rc = createCheck( p->uiH, uuid, divUuId, "myCheckId", kCheckId, "checkClass", "Check Me", true )) != kOkRC ) if((rc = createCheck( p->uiH, uuid, wsSessId, divUuId, "myCheckId", kCheckId, "checkClass", "Check Me", true )) != kOkRC )
goto errLabel; goto errLabel;
if((rc = createSelect( p->uiH, selUuId, divUuId, "mySelId", kSelectId, "selClass", "Select" )) != kOkRC ) if((rc = createSelect( p->uiH, selUuId, wsSessId, divUuId, "mySelId", kSelectId, "selClass", "Select" )) != kOkRC )
goto errLabel; goto errLabel;
if((rc = createOption( p->uiH, uuid, selUuId, "myOpt0Id", kOption0Id, "optClass", "Option 0" )) != kOkRC ) if((rc = createOption( p->uiH, uuid, wsSessId, selUuId, "myOpt0Id", kOption0Id, "optClass", "Option 0" )) != kOkRC )
goto errLabel; goto errLabel;
if((rc = createOption( p->uiH, uuid, selUuId, "myOpt1Id", kOption1Id, "optClass", "Option 1" )) != kOkRC ) if((rc = createOption( p->uiH, uuid, wsSessId, selUuId, "myOpt1Id", kOption1Id, "optClass", "Option 1" )) != kOkRC )
goto errLabel; goto errLabel;
if((rc = createOption( p->uiH, uuid, selUuId, "myOpt2Id", kOption2Id, "optClass", "Option 2" )) != kOkRC ) if((rc = createOption( p->uiH, uuid, wsSessId, selUuId, "myOpt2Id", kOption2Id, "optClass", "Option 2" )) != kOkRC )
goto errLabel; goto errLabel;
if((rc = createString( p->uiH, uuid, divUuId, "myStringId", kStringId, "stringClass", "String", "a string value" )) != kOkRC ) if((rc = createString( p->uiH, uuid, wsSessId, divUuId, "myStringId", kStringId, "stringClass", "String", "a string value" )) != kOkRC )
goto errLabel; goto errLabel;
if((rc = createNumber( p->uiH, uuid, divUuId, "myNumberId", kNumberId, "numberClass", "Number", 10, 0, 100, 1, 0 )) != kOkRC ) if((rc = createNumber( p->uiH, uuid, wsSessId, divUuId, "myNumberId", kNumberId, "numberClass", "Number", 10, 0, 100, 1, 0 )) != kOkRC )
goto errLabel; goto errLabel;
if((rc = createProgress( p->uiH, uuid, divUuId, "myProgressId", kProgressId, "progressClass", "Progress", 5, 0, 10 )) != kOkRC ) if((rc = createProgress( p->uiH, uuid, wsSessId, divUuId, "myProgressId", kProgressId, "progressClass", "Progress", 5, 0, 10 )) != kOkRC )
goto errLabel; goto errLabel;
if((rc = createFromFile( p->uiH, p->uiCfgFn, wsSessId )) != kOkRC )
goto errLabel;
errLabel: errLabel:
return rc; return rc;
} }
rc_t _handleUiValueMsg( ui_test_t* p, unsigned connId, unsigned parentAppId, unsigned uuId, unsigned appId, const value_t* v ) rc_t _handleUiValueMsg( ui_test_t* p, unsigned wsSessId, unsigned parentAppId, unsigned uuId, unsigned appId, const value_t* v )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
@ -105,26 +108,31 @@ namespace cw
} }
rc_t _uiTestCallback( void* cbArg, unsigned connId, opId_t opId, unsigned parentAppId, unsigned uuId, unsigned appId, const value_t* v ) // This function is called by the websocket with messages comring from a remote UI.
rc_t _uiTestCallback( void* cbArg, unsigned wsSessId, opId_t opId, unsigned parentAppId, unsigned uuId, unsigned appId, const value_t* v )
{ {
ui_test_t* p = (ui_test_t*)cbArg; ui_test_t* p = (ui_test_t*)cbArg;
switch( opId ) switch( opId )
{ {
case kConnectOpId: case kConnectOpId:
cwLogInfo("Connect: connId:%i.",connId); cwLogInfo("Connect: wsSessId:%i.",wsSessId);
break; break;
case kDisconnectOpId: case kDisconnectOpId:
cwLogInfo("Disconnect: connId:%i.",connId); cwLogInfo("Disconnect: wsSessId:%i.",wsSessId);
break; break;
case kInitOpId: case kInitOpId:
_uiTestCreateUi(p,connId); _uiTestCreateUi(p,wsSessId);
break; break;
case kRegisterOpId:
break;
case kValueOpId: case kValueOpId:
_handleUiValueMsg( p, connId, parentAppId, uuId, appId, v ); _handleUiValueMsg( p, wsSessId, parentAppId, uuId, appId, v );
break; break;
case kInvalidOpId: case kInvalidOpId:
@ -141,7 +149,7 @@ namespace cw
cw::rc_t cw::ui::test( ) cw::rc_t cw::ui::test( )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
const char* physRootDir = "/home/kevin/src/cw_rt/html/uiTest"; const char* physRootDir = "/home/kevin/src/cwtest/src/libcw/html/uiTest";
const char* dfltPageFn = "index.html"; const char* dfltPageFn = "index.html";
int port = 5687; int port = 5687;
unsigned rcvBufByteN = 2048; unsigned rcvBufByteN = 2048;
@ -151,6 +159,8 @@ cw::rc_t cw::ui::test( )
char sbuf[ sbufN+1 ]; char sbuf[ sbufN+1 ];
ui_test_t* ui = mem::allocZ<ui_test_t>(); ui_test_t* ui = mem::allocZ<ui_test_t>();
ui->uiCfgFn = "/home/kevin/src/cwtest/src/libcw/html/uiTest/ui.cfg";
if((rc = createUi(ui->uiH, port, _uiTestCallback, ui, physRootDir, dfltPageFn, websockTimeOutMs, rcvBufByteN, xmtBufByteN )) != kOkRC ) if((rc = createUi(ui->uiH, port, _uiTestCallback, ui, physRootDir, dfltPageFn, websockTimeOutMs, rcvBufByteN, xmtBufByteN )) != kOkRC )
return rc; return rc;

View File

@ -1,5 +1,6 @@
var _ws = null; var _ws = null;
var _dfltParentId = "uiDivId"; var _rootJsId = "uiDivId";
var _nextEleId = 0;
function set_app_title( suffix, className ) function set_app_title( suffix, className )
{ {
@ -164,8 +165,6 @@ function uiNumberSet( r )
} }
function dom_id_to_ele( id )
{ return document.getElementById(id); }
function dom_child_by_id( parentEle, child_id ) function dom_child_by_id( parentEle, child_id )
{ {
@ -194,11 +193,6 @@ function dom_set_option_by_text( ele_id, text )
} }
} }
function dom_set_checkbox( ele_id, fl )
{ dom_id_to_ele(ele_id).checked = fl }
function dom_get_checkbox( ele_id )
{ return dom_id_to_ele(ele_id).checked }
function dom_set_number( ele_id, val ) function dom_set_number( ele_id, val )
@ -206,14 +200,53 @@ function dom_set_number( ele_id, val )
dom_id_to_ele(ele_id).value = val dom_id_to_ele(ele_id).value = val
} }
//==============================================================================
function dom_id_to_ele( id )
{ return document.getElementById(id); }
function dom_set_checkbox( ele_id, fl )
{ dom_id_to_ele(ele_id).checked = fl }
function dom_get_checkbox( ele_id )
{ return dom_id_to_ele(ele_id).checked }
function dom_create_ele( ele_type )
{
ele = document.createElement(ele_type);
ele.id = _nextEleId;
_nextEleId += 1;
}
//==============================================================================
function ui_error( msg ) function ui_error( msg )
{ {
console.log("Error: " + msg ) console.log("Error: " + msg )
} }
function ui_send_value( ele, typeId, value )
{
if( ele.hasOwnProperty('uuId') )
ws_send("value " + ele.uuId + " " + typeId + " : " + value)
else
ui_error("A value msg send failed because the value had no UuId.");
}
function ui_send_bool_value( ele, value ) { ui_send_value(ele,'b',value); }
function ui_send_int_value( ele, value ) { ui_send_value(ele,'i',value); }
function ui_send_string_value( ele, value ) { ui_send_value(ele,'s',value); }
function ui_send_register( ele )
{
ws_send("register " + ele.parentJsId + " " + ele.jsId + " " + ele.id + " " + ele.uuId + " " + ele.appId )
}
function ui_print_children( eleId ) function ui_print_children( eleId )
{ {
var childrenL = document.getElementById(eleId).children var childrenL = dom_id_to_ele(eleId).children
for(var i=0; i<childrenL.length; i++) for(var i=0; i<childrenL.length; i++)
{ {
@ -224,9 +257,9 @@ function ui_print_children( eleId )
function ui_get_parent( parentId ) function ui_get_parent( parentId )
{ {
if( parentId==null || parentId.trim().length == 0 ) if( parentId==null || parentId.trim().length == 0 )
parentId = _dfltParentId parentId = _rootJsId
parent_ele = document.getElementById(parentId); parent_ele = dom_id_to_ele(parentId);
if( parent_ele == null ) if( parent_ele == null )
{ {
@ -240,16 +273,26 @@ function ui_get_parent( parentId )
function ui_create_ele( parent_ele, ele_type, d ) function ui_create_ele( parent_ele, ele_type, d )
{ {
// create the ctl object // create the ctl object
var ele = document.createElement(ele_type); var ele = dom_create_ele(ele_type);
if( ele == null ) if( ele == null )
ui_error("'%s' element create failed.", ele_type); ui_error("'%s' element create failed.", ele_type);
else else
{ {
ele.id = d.jsId; ele.jsId = d.jsId;
ele.uuId = d.uuId;
ele.appId = d.appId; ele.parentJsId = d.parentJsId;
ele.uuId = d.hasOwnProperty("uuId") ? d.uuId : null
ele.appId = d.hasOwnProperty("appId") ? d.appId : null
console.log("Created: " + ele_type + " parent:" + d.parentJsId + " id:" + ele.id + " appId:" + ele.appId + " uuId:" + ele.uuId)
parent_ele.appendChild(ele); parent_ele.appendChild(ele);
ui_send_register( ele );
} }
return ele return ele
} }
@ -257,7 +300,7 @@ function ui_create_ele( parent_ele, ele_type, d )
function ui_create_ctl( parent_ele, ele_type, label, d ) function ui_create_ctl( parent_ele, ele_type, label, d )
{ {
// create an enclosing div // create an enclosing div
var div_ele = document.createElement("div"); var div_ele = dom_create_ele("div");
div_ele.className = d.clas; div_ele.className = d.clas;
@ -268,7 +311,7 @@ function ui_create_ctl( parent_ele, ele_type, label, d )
// if label is not null then create an enclosing 'label' element // if label is not null then create an enclosing 'label' element
if( label != null ) if( label != null )
{ {
label_ele = document.createElement("label"); label_ele = dom_create_ele("label");
label_ele.innerHTML = label; label_ele.innerHTML = label;
@ -283,12 +326,11 @@ function ui_create_div( parent_ele, d )
{ {
var div_ele = ui_create_ele( parent_ele, "div", d ); var div_ele = ui_create_ele( parent_ele, "div", d );
if( div_ele != null ) if( div_ele != null )
{ {
div_ele.className = d.clas div_ele.className = d.clas
var p_ele = document.createElement("p") var p_ele = dom_create_ele("p")
if( d.title != null && d.title.length > 0 ) if( d.title != null && d.title.length > 0 )
p_ele.innerHTML = d.title p_ele.innerHTML = d.title
@ -296,7 +338,7 @@ function ui_create_div( parent_ele, d )
div_ele.appendChild( p_ele ) div_ele.appendChild( p_ele )
} }
return div_ele return div_ele;
} }
function ui_create_title( parent_ele, d ) function ui_create_title( parent_ele, d )
@ -311,10 +353,10 @@ function ui_create_button( parent_ele, d )
if( ele != null ) if( ele != null )
{ {
ele.innerHTML = d.title; ele.innerHTML = d.title;
ele.onclick = function() { _ws.send("value " + this.uuId + " i : 1"); } ele.onclick = function() { ui_send_int_value(this,1); }
} }
return ele return ele;
} }
function ui_create_check( parent_ele, d ) function ui_create_check( parent_ele, d )
@ -327,15 +369,14 @@ function ui_create_check( parent_ele, d )
dom_set_checkbox(ele.id, d.value ); dom_set_checkbox(ele.id, d.value );
ele.onclick = function() { _ws.send("value" + this.uuId + " b : " + dom_get_checkbox(this.id)); } ele.onclick = function() { ui_send_bool_value(this,dom_get_checkbox(this.id)); }
} }
return ele;
} }
function ui_on_select( ele ) function ui_on_select( ele )
{ {
var s = "value " + ele.uuId + " i : " + ele.options[ ele.selectedIndex ].appId ui_send_int_value(ele,ele.options[ ele.selectedIndex ].appId);
_ws.send( s );
} }
function ui_create_select( parent_ele, d ) function ui_create_select( parent_ele, d )
@ -365,7 +406,7 @@ function ui_create_string( parent_ele, d )
if( ele != null ) if( ele != null )
{ {
ele.value = d.value; ele.value = d.value;
ele.addEventListener('keyup', function(e) { if(e.keyCode===13){ _ws.send("value" + this.uuId + " s : " + this.value + "\0");} } ); ele.addEventListener('keyup', function(e) { if(e.keyCode===13){ ui_send_string_value(this, this.value); }} );
} }
} }
@ -377,7 +418,9 @@ function ui_number_keyup( e )
var ele = dom_id_to_ele(e.target.id) var ele = dom_id_to_ele(e.target.id)
console.log(ele.value) console.log(ele.value)
if( ele != null ) if( ele != null )
_ws.send("value" + ele.uuId + " i : " + ele.value); {
ui_send_int_value(ele,ele.value);
}
} }
} }
@ -394,7 +437,7 @@ function ui_create_number( parent_ele, d )
ele.decpl = d.decpl; ele.decpl = d.decpl;
ele.addEventListener('keyup', ui_number_keyup ); ele.addEventListener('keyup', ui_number_keyup );
} }
return ele;
} }
function ui_set_progress( ele_id, value ) function ui_set_progress( ele_id, value )
@ -415,70 +458,130 @@ function ui_create_progress( parent_ele, d )
ele.minValue = d.min; ele.minValue = d.min;
ui_set_progress( ele.id, d.value ); ui_set_progress( ele.id, d.value );
} }
return ele
} }
function ui_create( parentId, ele_type, d ) function ui_create( parentId, ele_type, d )
{ {
var parent_ele = ui_get_parent(parentId); var parent_ele = ui_get_parent(parentId);
var ele = null;
if( parent_ele != null ) if( parent_ele != null )
{ {
d.parentJsId = parentId
switch( ele_type ) switch( ele_type )
{ {
case "div": case "div":
ui_create_div( parent_ele, d ) ele = ui_create_div( parent_ele, d )
break; break;
case "title": case "title":
ui_create_title( parent_ele, d ) ele = ui_create_title( parent_ele, d )
break; break;
case "button": case "button":
ui_create_button( parent_ele, d ) ele = ui_create_button( parent_ele, d )
break; break;
case "check": case "check":
ui_create_check( parent_ele, d ) ele = ui_create_check( parent_ele, d )
break; break;
case "select": case "select":
ui_create_select( parent_ele, d ); ele = ui_create_select( parent_ele, d );
break; break;
case "option": case "option":
ui_create_option( parent_ele, d ); ele = ui_create_option( parent_ele, d );
break; break;
case "string": case "string":
ui_create_string( parent_ele, d ); ele = ui_create_string( parent_ele, d );
break; break;
case "number": case "number":
ui_create_number( parent_ele, d ); ele = ui_create_number( parent_ele, d );
break; break;
case "progress": case "progress":
ui_create_progress( parent_ele, d ); ele = ui_create_progress( parent_ele, d );
break; break;
default: default:
ui_error("Unknown UI element type: " + ele_type ) ui_error("Unknown UI element type: " + ele_type )
} }
if( d.hasOwnProperty("children") )
{
for (const ele_type in d.children)
ui_create( d.jsId, ele_type, d.children[ele_type] )
}
} }
} }
function ws_send( d ) function ui_uuid_to_ele( uuId, ele=null )
{
if( ele == null )
ele = dom_id_to_ele( _rootJsId )
if( ele.hasOwnProperty('uuId') )
{
if( ele.uuId == uuId )
return ele
for(var i=0; i<ele.childElementCount; ++i)
ui_uuid_to_ele( uuId, ele.children[i] )
}
ui_error("The element with uuid: " + uuId + " was not found.")
return null
}
// Do breadth first search for ele's without uuId's
function ui_next_ele_to_register( parentEle )
{
for(var i=0; i<parentEle.childElementCount; ++i)
{
var ele = parentEle.children[i]
if( ele.hasOwnProperty('uuId') && ele.uuId==null)
return ele
}
for(var i=0; i<parentEle.childElementCount; ++i)
{
var ele = ui_next_ele_to_register( parentEle.children[i])
if( ele != null )
return ele
}
return null
}
//
function ui_set_app_uuId( d )
{
console.log(d)
}
function ws_send( s )
{ {
s = JSON.stringify(d)
//console.log(s) //console.log(s)
_ws.send(s)
_ws.send(s+"\0")
} }
function ws_on_msg( jsonMsg ) function ws_on_msg( jsonMsg )
{ {
//console.log(jsonMsg) console.log(jsonMsg)
d = JSON.parse(jsonMsg.data); d = JSON.parse(jsonMsg.data);
switch( d.op ) switch( d.op )
@ -486,12 +589,18 @@ function ws_on_msg( jsonMsg )
case 'create': case 'create':
for (const ele_type in d.children) for (const ele_type in d.children)
{ {
ui_create( d.parent, ele_type, d.value[ele_type] ) ui_create( d.parent, ele_type, d.children[ele_type] )
//console.log(`${ele_type}: ${d.value[ele_type]}`); //console.log(`${ele_type}: ${d.value[ele_type]}`);
} }
break; break;
case 'set_app_id':
ui_set_app_uuId(d)
break;
default:
ui_error("Unknown UI operation. " + d.op )
} }
} }
@ -507,12 +616,35 @@ function ws_on_close()
set_app_title( "Disconnected", "title_disconnected" ); set_app_title( "Disconnected", "title_disconnected" );
} }
function ws_form_url(urlSuffix)
{
var pcol;
var u = document.URL;
pcol = "ws://";
if (u.substring(0, 4) === "http")
u = u.substr(7);
u = u.split("/");
return pcol + u[0] + "/" + urlSuffix;
}
function main() function main()
{ {
_ws = new WebSocket("ws://127.0.0.1:5687/","ui_protocol") rootEle = dom_id_to_ele(_rootJsId);
rootEle.uuId = 0;
rootEle.id = _nextEleId;
_nextEleId += 1;
console.log(ws_form_url(""))
_ws = new WebSocket(ws_form_url(""),"ui_protocol")
_ws.onmessage = ws_on_msg _ws.onmessage = ws_on_msg
_ws.onopen = ws_on_open _ws.onopen = ws_on_open
_ws.onclose = ws_on_close; _ws.onclose = ws_on_close;
} }

View File

@ -1,13 +1,18 @@
{ {
parent: "uiDivId"
div: { div: {
title:"My panel", jsId: "panelDivId",
class: "myPanelClass",
title: "My resource based panel",
children: { children: {
button:{ id:myBtnId, title:"Push Me" }, button:{ jsId: myBtn1Id, title:"Push Me" },
check:{ id:myCheckId, title:"Check Me" }, check:{ jsId: myCheck1Id, title:"Check Me" },
} }
} }