//| Copyright: (C) 2020-2024 Kevin Larke //| 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 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(); 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); }