libcw/cwUiTest.cpp
2024-12-01 14:35:24 -05:00

475 lines
13 KiB
C++

//| Copyright: (C) 2020-2024 Kevin Larke <contact AT larke DOT org>
//| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwMem.h"
#include "cwThread.h"
#include "cwWebSock.h"
#include "cwObject.h"
#include "cwUi.h"
#include "cwUiTest.h"
namespace cw
{
namespace ui
{
typedef struct ui_test_str
{
//const char* uiCfgFn; // Resource file name
srv::handle_t wsUiSrvH; //
std::atomic<bool> quitFl;
// Application values
bool appCheckFl;
unsigned appSelOptAppId;
int appInteger;
float appFloat;
int appProgress;
char* appString;
bool appCheck1Fl;
bool appCheck2Fl;
float appNumb;
unsigned appSelId;
unsigned myPanelUuId;
unsigned logUuId;
const object_t* listEleCfg;
} ui_test_t;
// Application Id's for UI elements
enum
{
// Programatically created UI elements
kDivId,
kCheckId,
kSelectId,
kOption0Id,
kOption1Id,
kOption2Id,
kOption3Id,
kStringId,
kIntegerId,
kFloatId,
kProgressId,
kLogId,
// Resource Based elements
kQuitBtnId,
kUiRptBtnId,
kPanelDivId,
kPanelBtn1Id,
kPanelCheck1Id,
kPanelBtn2Id,
kPanelCheck2Id,
kPanelFloaterId,
kSelId,
kOpt1Id,
kOpt2Id,
kOpt3Id,
kListId,
kStatusId
};
rc_t uiTestCreateUi( ui_test_t* p, unsigned wsSessId=kInvalidId )
{
rc_t rc = kOkRC;
unsigned uuid = kInvalidId;
unsigned selUuId = kInvalidId;
unsigned chanId = 0;
handle_t uiH = srv::uiHandle(p->wsUiSrvH);
// Create a UI elements programatically.
if((rc = createDiv( uiH, p->myPanelUuId, kInvalidId, "myDivId", kDivId, chanId, "divClass", "My Panel" )) != kOkRC )
goto errLabel;
if((rc = createCheck( uiH, uuid, p->myPanelUuId, "myCheckId", kCheckId, chanId, "checkClass", "Check Me", true )) != kOkRC )
goto errLabel;
if((rc = createSelect( uiH, selUuId, p->myPanelUuId, "mySelId", kSelectId, chanId, "selClass", "Select" )) != kOkRC )
goto errLabel;
if((rc = createOption( uiH, uuid, selUuId, "myOpt0Id", kOption0Id, chanId, "optClass", "Option 0" )) != kOkRC )
goto errLabel;
if((rc = createOption( uiH, uuid, selUuId, "myOpt1Id", kOption1Id, chanId, "optClass", "Option 1" )) != kOkRC )
goto errLabel;
if((rc = createOption( uiH, uuid, selUuId, "myOpt2Id", kOption2Id, chanId, "optClass", "Option 2" )) != kOkRC )
goto errLabel;
if((rc = createStr( uiH, uuid, p->myPanelUuId, "myStringId", kStringId, chanId, "stringClass", "String", "a string value" )) != kOkRC )
goto errLabel;
if((rc = createNumb( uiH, uuid, p->myPanelUuId, "myIntegerId", kIntegerId, chanId, "integerClass", "Integer", 0, 100, 1, 0, 10 )) != kOkRC )
goto errLabel;
if((rc = createNumb( uiH, uuid, p->myPanelUuId, "myFloatId", kFloatId, chanId, "floatClass", "Float", 0.53, 100.97, 1.0, 5, 10.0 )) != kOkRC )
goto errLabel;
if((rc = createProg( uiH, uuid, p->myPanelUuId, "myProgressId", kProgressId, chanId, "progressClass", "Progress", 0, 10, 5 )) != kOkRC )
goto errLabel;
if((rc = createLog( uiH, p->logUuId, p->myPanelUuId, "myLogId", kLogId, chanId, "logClass", "My Log (click toggles auto-scroll)" )) != kOkRC )
goto errLabel;
errLabel:
return rc;
}
void _print_state( ui_test_t* p )
{
printf("check:%i sel:%i int:%i flt:%f prog:%i str:%s chk1:%i chk2:%i numb:%f sel:%i\n",
p->appCheckFl,
p->appSelOptAppId,
p->appInteger,
p->appFloat,
p->appProgress,
p->appString,
p->appCheck1Fl,
p->appCheck2Fl,
p->appNumb,
p->appSelId);
}
rc_t _insert_list_ele( ui_test_t* p )
{
rc_t rc = kOkRC;
if( p->listEleCfg != nullptr )
{
handle_t uiH = srv::uiHandle(p->wsUiSrvH);
unsigned listUuId = findElementUuId( uiH, kListId );
printf("list uuid:%i\n",listUuId);
rc = createFromObject( uiH, p->listEleCfg, listUuId, kInvalidId );
}
return rc;
}
rc_t _insert_log_line( ui_test_t* p, const char* text )
{
return ui::setLogLine( srv::uiHandle(p->wsUiSrvH), p->logUuId, text );
}
rc_t _handleUiValueMsg( ui_test_t* p, unsigned wsSessId, unsigned parentAppId, unsigned uuId, unsigned appId, const value_t* v )
{
rc_t rc = kOkRC;
switch( appId )
{
case kQuitBtnId:
p->quitFl.store(true);
break;
case kUiRptBtnId:
ui::report(srv::uiHandle(p->wsUiSrvH));
break;
case kCheckId:
printf("Check:%i\n", v->u.b);
p->appCheckFl = v->u.b;
_insert_log_line( p, "check!\n" );
break;
case kSelectId:
printf("Selected: option appId:%i\n", v->u.i);
p->appSelOptAppId = v->u.i;
break;
case kStringId:
printf("String: %s\n",v->u.s);
mem::release(p->appString);
if( v->u.s != nullptr )
p->appString = mem::duplStr(v->u.s);
break;
case kIntegerId:
{
printf("Integer: %i\n",v->u.i);
p->appInteger = v->u.i;
handle_t uiH = srv::uiHandle(p->wsUiSrvH);
unsigned progUuId = parentAndAppIdToUuId( uiH, kDivId, kProgressId );
sendValueInt( uiH, progUuId, v->u.i );
}
break;
case kFloatId:
printf("Float: %f\n",v->u.f);
p->appFloat = v->u.f;
break;
case kPanelBtn1Id:
{
handle_t uiH = srv::uiHandle(p->wsUiSrvH);
unsigned btnUuId = parentAndAppIdToUuId( uiH, kPanelDivId, kPanelBtn2Id );
printf("Button 1 %i\n",btnUuId);
_print_state(p);
bool enableFl = isEnabled( uiH, btnUuId );
setEnable( uiH, btnUuId, !enableFl );
}
break;
case kPanelCheck1Id:
{
printf("check 2: %i\n",v->u.b);
p->appCheck1Fl = v->u.b;
handle_t uiH = srv::uiHandle(p->wsUiSrvH);
unsigned progUuId = parentAndAppIdToUuId( uiH, kDivId, kProgressId );
bool visibleFl = isVisible( uiH, progUuId );
setVisible( uiH, progUuId, !visibleFl );
}
break;
case kPanelBtn2Id:
printf("Button 2\n");
_print_state(p);
break;
case kPanelCheck2Id:
printf("check 1: %i\n",v->u.b);
p->appCheck1Fl = v->u.b;
_insert_list_ele( p );
break;
case kPanelFloaterId:
printf("numb: %f\n",v->u.f);
p->appNumb = v->u.f;
break;
case kSelId:
{
printf("sel: %i\n",v->u.i);
p->appSelId = v->u.i;
handle_t uiH = srv::uiHandle(p->wsUiSrvH);
unsigned uuId = parentAndAppIdToUuId( uiH, kPanelDivId, kStatusId );
const unsigned sN = 32;
char s[ sN ];
snprintf(s,sN,"sel:%i",v->u.i);
sendValueString( uiH, uuId, s );
}
break;
case kProgressId:
break;
default:
cwLogWarning("Unhandled value message: uuid:%i appId:%i\n",uuId,appId);
}
return rc;
}
//
rc_t _handleUiEchoMsg( ui_test_t* p, unsigned wsSessId, unsigned parentAppId, unsigned uuId, unsigned appId )
{
rc_t rc = kOkRC;
switch( appId )
{
case kCheckId:
sendValueBool( uiHandle( p->wsUiSrvH ), uuId, p->appCheckFl );
break;
case kSelectId:
sendValueInt( uiHandle( p->wsUiSrvH ), uuId, p->appSelOptAppId );
break;
case kStringId:
sendValueString( uiHandle( p->wsUiSrvH ), uuId, p->appString );
break;
case kIntegerId:
sendValueInt( uiHandle( p->wsUiSrvH ), uuId, p->appInteger );
break;
case kFloatId:
sendValueFloat( uiHandle( p->wsUiSrvH ), uuId, p->appFloat );
break;
case kProgressId:
sendValueInt( uiHandle( p->wsUiSrvH ), uuId, p->appProgress );
break;
case kPanelCheck1Id:
sendValueBool( uiHandle( p->wsUiSrvH ), uuId, p->appCheck1Fl);
break;
case kPanelCheck2Id:
sendValueBool( uiHandle( p->wsUiSrvH ), uuId, p->appCheck2Fl);
break;
case kPanelFloaterId:
sendValueFloat( uiHandle( p->wsUiSrvH ), uuId, p->appNumb );
break;
case kSelId:
sendValueInt( uiHandle( p->wsUiSrvH ), uuId, p->appSelId );
break;
}
return rc;
}
// This function is called by the websocket with messages coming from a remote UI.
rc_t _uiTestCallback( void* cbArg, unsigned wsSessId, opId_t opId, unsigned parentAppId, unsigned uuId, unsigned appId, unsigned chanId, const value_t* v )
{
ui_test_t* p = (ui_test_t*)cbArg;
switch( opId )
{
case kConnectOpId:
cwLogInfo("UI Test Connect: wsSessId:%i.",wsSessId);
break;
case kDisconnectOpId:
cwLogInfo("UI Test Disconnect: wsSessId:%i.",wsSessId);
break;
case kInitOpId:
//_uiTestCreateUi(p,wsSessId);
break;
case kValueOpId:
_handleUiValueMsg( p, wsSessId, parentAppId, uuId, appId, v );
break;
case kClickOpId:
{
printf("APP clicked. uu:%i app:%i ch:%i\n",uuId,appId,chanId);
handle_t uiH = srv::uiHandle(p->wsUiSrvH);
bool selectedFl = isSelected(uiH,uuId);
setSelect( uiH, uuId, !selectedFl );
}
break;
case kEchoOpId:
_handleUiEchoMsg( p, wsSessId, parentAppId, uuId, appId );
break;
case kInvalidOpId:
// fall through
default:
break;
}
return kOkRC;
}
}
}
cw::rc_t cw::ui::test( const object_t* cfg )
{
rc_t rc = kOkRC;
unsigned wsTimeOutMs = 50;
ui::ws::args_t args = {};
// Application Id's for the resource based UI elements.
appIdMap_t mapA[] =
{
{ kRootAppId, kQuitBtnId, "myQuitBtnId" },
{ kRootAppId, kPanelDivId, "panelDivId" },
{ kPanelDivId, kPanelBtn1Id, "myBtn1Id" },
{ kPanelDivId, kPanelCheck1Id, "myCheck1Id" },
{ kPanelDivId, kPanelBtn2Id, "myBtn2Id" },
{ kPanelDivId, kPanelCheck2Id, "myCheck2Id" },
{ kPanelDivId, kPanelFloaterId, "myFloater" },
{ kPanelDivId, kSelId, "mySel" },
{ kSelId, kOpt1Id, "myOpt1" },
{ kSelId, kOpt2Id, "myOpt2" },
{ kSelId, kOpt3Id, "myOpt3" },
{ kPanelDivId, kUiRptBtnId, "uiRptBtnId" },
{ kPanelDivId, kListId, "myListId" },
{ kPanelDivId, kStatusId, "status" }
};
unsigned mapN = sizeof(mapA)/sizeof(mapA[0]);
ui_test_t* app = mem::allocZ<ui_test_t>();
if( cfg == nullptr )
{
cwLogError(kInvalidArgRC,"ui::test() was not passed a valid cfg. object.");
goto errLabel;
}
if((rc = parseArgs(*cfg, args )) != kOkRC )
{
cwLogError(rc,"UI parse args failed in ui::test()");
goto errLabel;
}
app->quitFl.store(false);
// Initial values for the test applications
app->appCheckFl = true;
app->appSelOptAppId = kOption1Id;
app->appInteger = 5;
app->appFloat = 2.56;
app->appProgress = 7;
app->appString = mem::duplStr("fooz");
app->appCheck1Fl = false;
app->appCheck2Fl = true;
app->appNumb = 1.23;
app->appSelId = kOpt3Id;
if((rc = cfg->getv( "listEle", app->listEleCfg )) != kOkRC )
rc = cwLogError( rc, "The list element cfg. was not found.");
//app->uiCfgFn = "/home/kevin/src/cwtest/src/libcw/html/uiTest/ui.cfg";
// create the UI server
if((rc = srv::create(app->wsUiSrvH, args, app, _uiTestCallback, mapA, mapN, wsTimeOutMs, nullptr )) != kOkRC )
return rc;
if((rc = uiTestCreateUi( app )) != kOkRC )
goto errLabel;
//ui::report( srv::uiHandle(app->wsUiSrvH) );
// start the UI server
if((rc = srv::start(app->wsUiSrvH)) != kOkRC )
goto errLabel;
// readline loop
while( !app->quitFl.load() )
{
sleepMs(50);
}
errLabel:
ui::ws::releaseArgs(args);
rc_t rc1 = kOkRC;
if( app->wsUiSrvH.isValid() )
rc1 = srv::destroy(app->wsUiSrvH);
mem::release(app->appString);
mem::release(app);
return rcSelect(rc,rc1);
}