*/}}
Browse Source

Example browser

Yiming Wu 2 years ago
parent
commit
e87eed3a1d
6 changed files with 663 additions and 354 deletions
  1. 37 6
      CMakeLists.txt
  2. 33 344
      calculator.c
  3. 253 0
      example_viewer.c
  4. 319 0
      fruits.c
  5. 1 4
      modelling_main.c
  6. 20 0
      simplest.c

+ 37 - 6
CMakeLists.txt

@@ -9,19 +9,50 @@ include_directories(
 
 add_definitions(-w)
 
-set(DemoFiles 
-    ${CMAKE_SOURCE_DIR}/main.c
+set(SimplestFiles 
+    ${CMAKE_SOURCE_DIR}/simplest.c
+)
+set(FruitsFiles 
+    ${CMAKE_SOURCE_DIR}/fruits.c
 )
 set(ModellingFiles 
     ${CMAKE_SOURCE_DIR}/modelling_main.c
 )
+set(CalculatorFiles 
+    ${CMAKE_SOURCE_DIR}/calculator.c
+)
+
+set(ExampleViewerFiles 
+    ${CMAKE_SOURCE_DIR}/example_viewer.c
+)
 
-add_executable(demo ${DemoFiles})
-add_executable(modelling_demo ${ModellingFiles})
+add_executable(simplest ${SimplestFiles})
+add_executable(fruits ${FruitsFiles})
+add_executable(modelling_main ${ModellingFiles})
+add_executable(calculator ${CalculatorFiles})
+add_executable(example_viewer ${ExampleViewerFiles})
 
-target_link_libraries(demo
+target_link_libraries(simplest
+    ${LAGUI_SHARED_LIBS}
+)
+target_link_libraries(fruits
     ${LAGUI_SHARED_LIBS}
 )
-target_link_libraries(modelling_demo
+target_link_libraries(modelling_main
     ${LAGUI_SHARED_LIBS}
 )
+target_link_libraries(calculator
+    ${LAGUI_SHARED_LIBS}
+)
+
+target_link_libraries(example_viewer
+    ${LAGUI_SHARED_LIBS}
+)
+
+add_custom_command(
+    TARGET example_viewer POST_BUILD
+    COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/example_source_files
+    COMMAND cp ${CMAKE_SOURCE_DIR}/calculator.c     ${CMAKE_CURRENT_BINARY_DIR}/example_source_files/calculator.c
+    COMMAND cp ${CMAKE_SOURCE_DIR}/fruits.c         ${CMAKE_CURRENT_BINARY_DIR}/example_source_files/fruits.c
+    COMMAND cp ${CMAKE_SOURCE_DIR}/modelling_main.c ${CMAKE_CURRENT_BINARY_DIR}/example_source_files/modelling_main.c
+    COMMAND cp ${CMAKE_SOURCE_DIR}/simplest.c       ${CMAKE_CURRENT_BINARY_DIR}/example_source_files/simplest.c)

+ 33 - 344
main.c → calculator.c

@@ -2,37 +2,6 @@
 
 extern LA MAIN;
 
-STRUCTURE(Bowl){
-    laListItem    Item;
-    laSafeString* Name;
-    laListHandle  Fruits;
-};
-STRUCTURE(Basket){
-    int pad;
-    int example;
-    laSafeString* name;
-    laListHandle Stuff;
-    laListHandle Bowls;
-};
-#define FRUIT_TYPE_APPLE 1
-#define FRUIT_TYPE_PEAR 2
-STRUCTURE(Fruit){
-    laListItem   Item;
-    int          Type;
-    Fruit*       WhyNot;
-    laListHandle*  Parent;
-    Bowl*        Container;
-    laListHandle Bundled;
-};
-STRUCTURE(Apple){
-    Fruit  Base;
-    real   HowSweet;
-};
-STRUCTURE(Pear){
-    Fruit Base;
-    int Really;
-};
-
 STRUCTURE(Calculator){
     laListHandle Item;
     laSafeString* Title;
@@ -82,21 +51,17 @@ STRUCTURE(CalcRefNode){
 
 #define CALC_NODE_GENERIC 0
 #define CALC_NODE_NUMBER 1
-#define CALC_NODE_OP     2
+#define CALC_NODE_     2
 #define CALC_NODE_MIX    3
 #define CALC_NODE_RESULT 4
 #define CALC_NODE_REF    5
-#define CALC_OP_ADD 0
-#define CALC_OP_SUB 1
-#define CALC_OP_MUL 2
-#define CALC_OP_DIV 3
+#define CALC__ADD 0
+#define CALC__SUB 1
+#define CALC__MUL 2
+#define CALC__DIV 3
 
-Basket B={0};
 CalculatorCollection* CC=0;
 
-laPropContainer* pcApple,*pcPear;
-laProp* pBasket, *pFruit, *pBowl;
-
 laPropContainer* pcNumber,*pcOp,*pcMix,*pcResult,*pcGeneric,*pcRef;
 
 laTheme* LightTheme, *DarkTheme;
@@ -169,9 +134,10 @@ void UiCalcRefNode(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, l
     laEndRow(uil,b);
     laShowItemFull(uil,c,This,"ref",LA_WIDGET_COLLECTION_SELECTOR,0,laui_IdentifierOnly,0);
 }
+
 void UiRack(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil);
-    laShowItem(uil,c,This,"nodes")->Flags|=LA_UI_FLAGS_NO_DECAL;;
+    laShowItem(uil,c,This,"nodes")->Flags|=LA_UI_FLAGS_NO_DECAL;
     laShowItem(uil,c,This,"add_node");
 }
 void UiCalculator(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
@@ -190,12 +156,17 @@ void UiCalculator(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, la
 }
 void CalcPanel(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laColumn* cl,*cr; laSplitColumn(uil,c,0.5); cl=laLeftColumn(c,0); cr=laRightColumn(c,0);
+    laShowLabel(uil,c,"You can use \"Data\" panel to see lagui-property structure.",0,0)->Flags|=LA_TEXT_LINE_WRAP;
     laShowItemFull(uil,cl,0,"calc.current",LA_WIDGET_COLLECTION_SELECTOR,0,laui_IdentifierOnly,0);
     laShowItem(uil,cr,0,"CALC_add_calculator");
     laShowItemFull(uil,c,0,"calc.current",LA_WIDGET_COLLECTION_SINGLE,0,0,0)->Flags|=LA_UI_COLLECTION_NO_HIGHLIGHT;
 }
+void CalcPanelRegular(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+    laShowItemFull(uil,c,0,"calc.current",0,0,laui_SubPropInfoDefault,1);
+}
 
-int OPINV_AddCalculator(laOperator* a, laEvent* e){
+int INV_AddCalculator(laOperator* a, laEvent* e){
     Calculator* c=memAcquireHyper(sizeof(Calculator));
     strSafeSet(&c->Title, "New Calculator");
     lstAppendItem(&CC->Calculators,c);
@@ -204,7 +175,7 @@ int OPINV_AddCalculator(laOperator* a, laEvent* e){
     laRecordAndPushProp(0, "calc"); laNotifyUsers("calc"); laPrintDBInstInfo();
     return LA_FINISHED;
 }
-int OPINV_AddRack(laOperator* a, laEvent* e){
+int INV_AddRack(laOperator* a, laEvent* e){
     Calculator* c=a->This?a->This->EndInstance:CC->CurrentCalculator; if(!c) return LA_FINISHED;
     CalcRack* cr=memAcquire(sizeof(CalcRack));
 
@@ -223,12 +194,12 @@ void AddNodesPanel(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, l
     laShowItemFull(uil,c,0,"LA_confirm",0,"icon=😋;text=Result;feedback=RESULT;",0,0);
     laShowItemFull(uil,c,0,"LA_confirm",0,"icon=🡬;text=Reference;feedback=REF;",0,0);
 }
-int OPINV_AddNode_(laOperator* a, laEvent* e){
+int INV_AddNode_(laOperator* a, laEvent* e){
     CalcRack* cr=a->This?a->This->EndInstance:0; if(!cr) return LA_FINISHED;
     laEnableOperatorPanel(a, a->This, e->x,e->y,0,0,0,0,0,0,0,0,0,0,e);
     return LA_RUNNING;
 }
-int OPMOD_AddNode_(laOperator* a, laEvent* e){
+int MOD_AddNode_(laOperator* a, laEvent* e){
     if(!a->This){ return LA_FINISHED; }
     CalcRack* cr=a->This->EndInstance;
     if(a->ConfirmData){
@@ -237,7 +208,7 @@ int OPMOD_AddNode_(laOperator* a, laEvent* e){
             n=memAcquire(sizeof(CalcNumberNode)); CalcNumberNode* cn=n; n->Type=CALC_NODE_NUMBER;
             cn->Base.OutA=laCreateOutSocket(cn, "A", 0);
         }elif(strSame(a->ConfirmData->StrData, "CALC")){
-            n=memAcquire(sizeof(CalcOpNode)); CalcOpNode* cn=n; n->Type=CALC_NODE_OP;
+            n=memAcquire(sizeof(CalcOpNode)); CalcOpNode* cn=n; n->Type=CALC_NODE_;
             cn->Base.OutA=laCreateOutSocket(cn, "Out", 0);
             cn->inA=laCreateInSocket("A", 0);
             cn->inB=laCreateInSocket("B", 0);
@@ -269,7 +240,7 @@ int OPMOD_AddNode_(laOperator* a, laEvent* e){
 laPropContainer* CalcGetNodeType(CalcNode* n){
     switch(n->Type){
     case CALC_NODE_NUMBER: return pcNumber;
-    case CALC_NODE_OP: return pcOp;
+    case CALC_NODE_: return pcOp;
     case CALC_NODE_MIX: return pcMix;
     case CALC_NODE_RESULT: return pcResult;
     case CALC_NODE_REF: return pcRef;
@@ -308,10 +279,11 @@ int RegisterCalculator(){
     CC=memAcquire(sizeof(CalculatorCollection));
 
     laRegisterUiTemplate("panel_calc", "Calculator", CalcPanel, 0, 0,0);
+    laRegisterUiTemplate("panel_calc_regular", "Data", CalcPanelRegular, 0, 0,0);
 
-    laCreateOperatorType("CALC_add_calculator", "Add Calculator", "Add a calculator", 0,0,0,OPINV_AddCalculator,0,'+',0);
-    laCreateOperatorType("CALC_add_rack", "Add Rack", "Add a rack into the calculator", 0,0,0,OPINV_AddRack,0,'+',0);
-    laCreateOperatorType("CALC_add_node", "Add Node", "Add a fruit into the basket or bundle", 0,0,0,OPINV_AddNode_,OPMOD_AddNode_,'+',0)->UiDefine=AddNodesPanel;
+    laCreateOperatorType("CALC_add_calculator", "Add Calculator", "Add a calculator", 0,0,0,INV_AddCalculator,0,'+',0);
+    laCreateOperatorType("CALC_add_rack", "Add Rack", "Add a rack into the calculator", 0,0,0,INV_AddRack,0,'+',0);
+    laCreateOperatorType("CALC_add_node", "Add Node", "Add a fruit into the basket or bundle", 0,0,0,INV_AddNode_,MOD_AddNode_,'+',0)->UiDefine=AddNodesPanel;
 
     laPropContainer* pc=laDefineRoot();
     laAddSubGroup(pc, "calc", "Calculator Collections", "The collection of calculators", "calc_collection",0,0,0,-1,CalcGetCollection,0,0,0,0,0,0,LA_UDF_SINGLE|LA_UDF_LOCAL);
@@ -330,7 +302,7 @@ int RegisterCalculator(){
 
     pc=laAddPropertyContainer("calc_rack", "Rack", "A node rack", 0, UiRack, sizeof(CalcRack), 0, 0, 1);
     p= laAddSubGroup(pc, "nodes", "Nodes","Nodes on this rack","calc_node",CalcGetNodeType,0,0,-1,0,0,0,0,0,0,offsetof(CalcRack,Nodes),0);
-    laSubGroupExtraFunctions(p,0,CalcGetTheme,CalcGetGap);
+    laSubGroupExtraFunctions(p,0,CalcGetTheme,CalcGetGap,0);
     laAddOperatorProperty(pc,"add_node", "Add Node", "Add a node", "CALC_add_node", 0, 0);
 
     pcGeneric=pc=laAddPropertyContainer("calc_node", "Node", "A calculator node", 0, 0, sizeof(CalcNode), 0, 0, 1);
@@ -338,7 +310,7 @@ int RegisterCalculator(){
     p=laAddEnumProperty(pc,"type","Type","Type",0,0,0,0,0,offsetof(CalcNode,Type),0,0,0,0,0,0,0,0,0,LA_AS_IDENTIFIER|LA_READ_ONLY);
     laAddEnumItemAs(p,"GENERIC","Generic","Generic", CALC_NODE_GENERIC, 0);
     laAddEnumItemAs(p,"NUMBER","Number","Number", CALC_NODE_NUMBER, 0);
-    laAddEnumItemAs(p,"OP","Op","Op", CALC_NODE_OP, 0);
+    laAddEnumItemAs(p,"","Op","Op", CALC_NODE_, 0);
     laAddEnumItemAs(p,"MIX","Mix","Mix", CALC_NODE_MIX, 0);
     laAddEnumItemAs(p,"RESULT","Result","Result", CALC_NODE_RESULT, 0);
     laAddEnumItemAs(p,"REF","Reference","Reference", CALC_NODE_REF, 0);
@@ -355,10 +327,10 @@ int RegisterCalculator(){
     pcOp=pc=laAddPropertyContainer("calc_op_node", "Op Node", "A node that does an operation between two values", 0, UiCalcOpNode, sizeof(CalcOpNode), 0, 0, 1);
     laAddSubGroup(pc, "base", "Base","Base","calc_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
     p=laAddEnumProperty(pc,"operation","Operation","Operation on two values",0,0,0,0,0,offsetof(CalcOpNode,Operation),0,0,0,0,0,0,0,0,0,0);
-    laAddEnumItemAs(p,"ADD","Add","A + B", CALC_OP_ADD,0);
-    laAddEnumItemAs(p,"SUB","Subtract","A - B", CALC_OP_SUB,0);
-    laAddEnumItemAs(p,"MUL","Multiply","A * B", CALC_OP_MUL,0);
-    laAddEnumItemAs(p,"DIV","Divide","A / B", CALC_OP_DIV,0);
+    laAddEnumItemAs(p,"ADD","Add","A + B", CALC__ADD,0);
+    laAddEnumItemAs(p,"SUB","Subtract","A - B", CALC__SUB,0);
+    laAddEnumItemAs(p,"MUL","Multiply","A * B", CALC__MUL,0);
+    laAddEnumItemAs(p,"DIV","Divide","A / B", CALC__DIV,0);
     laAddSubGroup(pc, "in_a", "A","Input value A","la_in_socket",0,0,0,offsetof(CalcOpNode,inA),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc, "in_b", "B","Input value B","la_in_socket",0,0,0,offsetof(CalcOpNode,inB),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddFloatProperty(pc,"val_a", "A", "Internal value A", 0,0,0,0,0,0,0,0,offsetof(CalcOpNode,AVal),0,0,0,0,0,0,0,0,0,0,0);
@@ -381,310 +353,27 @@ int RegisterCalculator(){
     laSaveProp("calc");
 }
 
-
-
-
-int OPINV_AddBowl(laOperator* a, laEvent* e){
-    Bowl* b=memAcquireHyper(sizeof(Bowl));
-    lstAppendItem(&B.Bowls,b);
-
-    laRecordAndPushProp(0, "basket");laNotifyUsers("basket");
-    laPrintDBInstInfo();
-
-    return LA_FINISHED;
-}
-int OPINV_RemoveBowl(laOperator* a, laEvent* e){
-    Bowl* b=a->This?a->This->EndInstance:B.Bowls.pLast; if(!b) return LA_FINISHED;
-    
-    lstRemoveItem(&B.Bowls, b);
-    memFree(b);
-
-    laRecordAndPushProp(0, "basket");laNotifyUsers("basket");
-    laPrintDBInstInfo();
-
-    return LA_FINISHED;
-}
-int OPINV_AddFruit(laOperator* a, laEvent* e){
-    laListHandle* into=(a->This&&a->This->LastPs->p==pBowl)?&((Bowl*)a->This->EndInstance)->Fruits:0;
-    if(!into) into=a->This?&((Fruit*)a->This->EndInstance)->Bundled:0;
-
-    char* stype; int size=sizeof(Apple); int type=FRUIT_TYPE_APPLE;
-    if(stype=strGetArgumentString(a->ExtraInstructionsP,"type")){
-        if(!strcmp(stype,"pear")) {size=sizeof(Pear);type=FRUIT_TYPE_PEAR;}
-    }
-    Fruit* f=memAcquire(size); f->Type=type;
-    if(into){ lstAppendItem(into, f); f->Parent=into; laNotifyUsers("basket"); /*laNotifyUsersPPPath(a->This,"base.bundled");*/ }
-    else{ lstAppendItem(&B.Stuff, f); laNotifyUsers("basket");}
-
-    laRecordAndPushProp(0, "basket");
-    laPrintDBInstInfo();
-
-    return LA_FINISHED;
-}
-void DestroyFruit(Fruit* f){
-    Fruit* NextF;
-    for(Fruit*fi=f->Bundled.pFirst;fi;fi=NextF){
-        NextF=fi->Item.pNext;
-        DestroyFruit(fi);
-    }
-    memFree(f);
-}
-int OPINV_RemoveFruit(laOperator* a, laEvent* e){
-    Fruit* f=a->This?a->This->EndInstance:0; laListHandle* l;
-    
-    if(f->Parent){ l=f->Parent;lstRemoveItem(l,f); laNotifyUsers("basket"); }
-    else{ l=&B.Stuff; lstRemoveItem(l,f);laNotifyUsers("basket"); }
-
-    DestroyFruit(f);
-
-    laRecordAndPushProp(0, "basket");
-
-    return LA_FINISHED;
-}
-int OPINV_MoveFruit(laOperator* a, laEvent* e){
-    Fruit* f=a->This?a->This->EndInstance:0; laListHandle* l; int direction=0; char* dir;
-    if(dir=strGetArgumentString(a->ExtraInstructionsP,"direction")){
-        if(!strcmp(dir,"up")) {direction=1;}
-    }
-    
-    if(f->Parent){ l=f->Parent; laNotifyUsers("basket"); /*laNotifyUsersPPPath(a->This,"base.parent.bundled");*/ }
-    else{ l=&B.Stuff; laNotifyUsers("basket"); }
-
-    if(direction) lstMoveUp(l,f); 
-    else lstMoveDown(l,f); 
-
-    laRecordAndPushProp(0, "basket");
-
-    return LA_FINISHED;
-}
-int OPINV_PushBasketState(laOperator* a, laEvent* e){
-    laRecordAndPushProp(0, "basket");
-    logPrint("Pushed state: \"basket\"\n");
-    return LA_FINISHED;
-}
-
-laPropContainer* FruitGetType(Fruit* f){
-    if(f->Type==FRUIT_TYPE_APPLE) return pcApple;
-    if(f->Type==FRUIT_TYPE_PEAR) return pcPear;
-    return pcApple;
-}
-
-void* FruitGetBasket(void* none){
-    return &B;
-}
-
-void* BowlGetFirst(void* none, void *UNUSED){
-    return B.Bowls.pFirst;
-}
-void* FruitGetFirst(void* none, void *UNUSED){
-    return B.Stuff.pFirst;
-}
-
-void FruitsPanel(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
-    laColumn* c=laFirstColumn(uil);
-    laColumn* cl;
-    laSplitColumn(uil,c,0.5);
-    cl=laLeftColumn(c,0);
-    
-    laShowItem(uil,c,0,"basket");
-    
-    laUiItem* r=laBeginRow(uil,c,0,0);
-    strSafeSet(&laShowItem(uil,c,0,"FRUIT_add")->ExtraInstructions,"type=apple;icon=🍏;text=Add Apple;");
-    strSafeSet(&laShowItem(uil,c,0,"FRUIT_add")->ExtraInstructions,"type=pear;icon=🍐;text=Add Pear;");
-    laShowItem(uil,c,0,"BOWL_add");
-    laEndRow(uil,r);
-    laShowSeparator(uil,c);
-
-    laShowItemFull(uil,c,0,"la.example_string", LA_WIDGET_STRING_MULTI, 0 ,0,0);
-
-    r=laBeginRow(uil,c,0,0);
-    laShowItem(uil,c,0,"LA_undo");
-    laShowItem(uil,c,0,"LA_redo");
-    laShowItem(uil,c,0,"STATE_push");
-    laEndRow(uil,r);
-}
-void UIBowl(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
-    laColumn* c=laFirstColumn(uil);
-    laColumn* cl,*cr;
-    laSplitColumn(uil,c,0.4);
-    cl=laLeftColumn(c, 1);
-    cr=laRightColumn(c,0);
-    laUiItem*u;
-
-    laUiItem* r=laBeginRow(uil,cr,1,0);
-    laShowItem(uil,cr,This,"name")->Expand=1;
-    laShowItem(uil,cr,This,"remove")->Flags|=LA_UI_FLAGS_ICON;
-    laEndRow(uil,r);
-    
-    r=laOnConditionToggle(uil,cl,0,0,0,0,0);{
-        laShowItem(uil,cr,This,"fruits");
-        laUiItem* r1=laBeginRow(uil,cr,0,0);
-        strSafeSet(&laShowItem(uil,cr,This,"add_fruit")->ExtraInstructions,"type=apple;icon=🍏;text=Add Apple;");
-        strSafeSet(&laShowItem(uil,cr,This,"add_fruit")->ExtraInstructions,"type=pear;icon=🍐;text=Add Pear;");
-        laEndRow(uil,r1);
-    }laEndCondition(uil,r);
-}
-void UIApple(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
-    laColumn* c=laFirstColumn(uil);
-    laColumn* cl,*cr;
-    laSplitColumn(uil,c,0.4);
-    cl=laLeftColumn(c, 1);
-    cr=laRightColumn(c,0);
-    laUiItem*u;
-
-    laUiItem* r=laBeginRow(uil,cr,1,0);
-    laShowLabel(uil,cr,"Apple",0,0)->Expand=1;
-    laShowItem(uil,cr,This,"how_sweet")->Expand=1;
-    laShowItem(uil,cr,This,"base.why_not")->Expand=1;
-    laShowItem(uil,cr,This,"base.container")->Expand=1;
-    laShowItem(uil,cr,This,"base.remove")->Flags|=LA_UI_FLAGS_ICON;
-    u=laShowItem(uil,cr,This,"base.move"); strSafeSet(&u->ExtraInstructions,"direction=up;icon=🡹;");u->Flags|=LA_UI_FLAGS_ICON;
-    u=laShowItem(uil,cr,This,"base.move"); strSafeSet(&u->ExtraInstructions,"direction=down;icon=🡻;");u->Flags|=LA_UI_FLAGS_ICON;
-    laEndRow(uil,r);
-    r=laOnConditionToggle(uil,cl,0,0,0,0,0);{
-        laShowItem(uil,cr,This,"base.bundled");
-        laUiItem* r1=laBeginRow(uil,cr,0,0);
-        strSafeSet(&laShowItem(uil,cr,This,"base.add")->ExtraInstructions,"type=apple;icon=🍏;text=Add Apple;");
-        strSafeSet(&laShowItem(uil,cr,This,"base.add")->ExtraInstructions,"type=pear;icon=🍐;text=Add Pear;");
-        laEndRow(uil,r1);
-    }laEndCondition(uil,r);
-}
-void UIPear(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
-    laColumn* c=laFirstColumn(uil);
-    laColumn* cl,*cr;
-    laSplitColumn(uil,c,0.4);
-    cl=laLeftColumn(c, 1);
-    cr=laRightColumn(c,0);
-    laUiItem*u;
-
-    laUiItem* r=laBeginRow(uil,cr,1,0);
-    laShowLabel(uil,cr,"Pear",0,0)->Expand=1;
-    laShowItem(uil,cr,This,"really")->Expand=1;
-    laShowItem(uil,cr,This,"base.why_not")->Expand=1;
-    laShowItem(uil,cr,This,"base.container")->Expand=1;
-    laShowItem(uil,cr,This,"base.remove")->Flags|=LA_UI_FLAGS_ICON;
-    u=laShowItem(uil,cr,This,"base.move"); strSafeSet(&u->ExtraInstructions,"direction=up;icon=🡹;");u->Flags|=LA_UI_FLAGS_ICON;
-    u=laShowItem(uil,cr,This,"base.move"); strSafeSet(&u->ExtraInstructions,"direction=down;icon=🡻;");u->Flags|=LA_UI_FLAGS_ICON;
-    laEndRow(uil,r);
-    r=laOnConditionToggle(uil,cl,0,0,0,0,0);{
-        laShowItem(uil,cr,This,"base.bundled");
-        laUiItem* r1=laBeginRow(uil,cr,0,0);
-        strSafeSet(&laShowItem(uil,cr,This,"base.add")->ExtraInstructions,"type=apple;icon=🍏;text=Add Apple;");
-        strSafeSet(&laShowItem(uil,cr,This,"base.add")->ExtraInstructions,"type=pear;icon=🍐;text=Add Pear;");
-        laEndRow(uil,r1);
-    }laEndCondition(uil,r);
-}
-void UIBowlSimple(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
-    laColumn* c=laFirstColumn(uil);
-    laShowItemFull(uil,c,This,"name",LA_WIDGET_STRING_PLAIN, 0 ,0,0);
-}
-void UIFruitSimple(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
-    laColumn* c=laFirstColumn(uil);
-    laShowItem(uil,c,This,"type");
-}
-
-void ScenePanel(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
-    laColumn* c=laFirstColumn(uil);
-    
-    laUiItem* ui=laShowCanvas(uil,c,0,"tns.world",0,-1);
-    laDefault3DViewOverlay(ui);
-}
-
-int RegisterEverything(){
-    laRegisterUiTemplate("panel_fruit", "Fruits", FruitsPanel, 0, 0,0);
-    laRegisterUiTemplate("panel_scene", "Scene", ScenePanel, 0, 0,0);
-
-    laCreateOperatorType("BOWL_add", "Add Bowl", "Add a bowl", 0,0,0,OPINV_AddBowl,0,'+',0);
-    laCreateOperatorType("BOWL_remove", "Remove Bowl", "Remove a bowl", 0,0,0,OPINV_RemoveBowl,0,'-',0);
-    laCreateOperatorType("FRUIT_add", "Add Fruit", "Add a fruit into the basket or bundle", 0,0,0,OPINV_AddFruit,0,'+',0);
-    laCreateOperatorType("FRUIT_remove", "Remove Fruit", "Remove a fruit", 0,0,0,OPINV_RemoveFruit,0,'-',0);
-    laCreateOperatorType("FRUIT_move", "Move Fruit", "Move a fruit", 0,0,0,OPINV_MoveFruit,0,'~',0);
-    laCreateOperatorType("STATE_push", "Push State", "Push basket state", 0,0,0,OPINV_PushBasketState,0,0,0);
-
-    laPropContainer* pc=laDefineRoot();
-    laAddSubGroup(pc, "basket", "Basket", "The basket", "basket",0,0,0,-1,FruitGetBasket,0,0,0,0,0,0,LA_UDF_SINGLE|LA_UDF_LOCAL);
-    laProp*p;
-    
-    pc=laAddPropertyContainer("bowl", "Bowl", "Some sort of a bowl", 0, UIBowl, sizeof(Bowl), 0, 0, 2);
-    laAddStringProperty(pc,"name","Name","Name of the bowl",0,0,0,0,1,offsetof(Bowl,Name),0,0,0,0,LA_AS_IDENTIFIER);
-    laAddSubGroup(pc, "fruits", "Fruits","Fruits","fruit",FruitGetType,0,0,-1,0,0,0,0,0,0,offsetof(Bowl,Fruits),0);
-    laAddOperatorProperty(pc,"remove","Remove","Remove a bowl", "BOWL_remove", '-', 0);
-    laAddOperatorProperty(pc,"add_fruit","Add","Add a fruit into the bowl", "FRUIT_add", '+', 0);
-
-    pc=laAddPropertyContainer("basket", "Basket", "A basket of fruits", 0, 0, sizeof(Basket), 0, 0, 1|LA_UDF_LOCAL|LA_PROP_OTHER_ALLOC|LA_UDF_SINGLE);
-    laAddIntProperty(pc,"example","Example","Example int",0,0,0,0,0,1,0,0,offsetof(Basket,example),0,0,0,0,0,0,0,0,0,0,0);
-    laAddStringProperty(pc,"name","Name","Name of the basket",0,0,0,0,1,offsetof(Basket,name),0,0,0,0,LA_UDF_REFER);
-    laAddSubGroup(pc, "stuff", "Stuff","Stuffs","fruit",FruitGetType,0,0,-1,0,0,0,0,0,0,offsetof(Basket,Stuff),0);
-    pBowl = laAddSubGroup(pc, "bowls", "Bows","Bows","bowl",0,0,0,-1,0,0,0,0,0,0,offsetof(Basket,Bowls),0);
-
-    pc=laAddPropertyContainer("fruit", "Fruit", "A fruit", 0, 0, sizeof(Fruit), 0, 0, 1);
-    p=laAddEnumProperty(pc,"type","Type","Type",0,0,0,0,0,offsetof(Fruit,Type),0,0,0,0,0,0,0,0,0,LA_AS_IDENTIFIER|LA_READ_ONLY);
-    laAddEnumItemAs(p,"APPLE","Apple","Apple", FRUIT_TYPE_APPLE, L'🍏');
-    laAddEnumItemAs(p,"PEAR","Pear","Pear", FRUIT_TYPE_PEAR, L'🍐');
-    pFruit = laAddSubGroup(pc, "bundled", "Bundled","Bundled","fruit",FruitGetType,0,0,-1,0,0,0,0,0,0,offsetof(Fruit,Bundled),0);
-    laAddSubGroup(pc, "why_not", "Why Not","Why not try","fruit",0,LA_WIDGET_COLLECTION_SELECTOR,UIFruitSimple,offsetof(Fruit,WhyNot),FruitGetFirst,0,laget_ListNext,0,0,0,0,LA_UDF_REFER);
-    laAddSubGroup(pc, "parent", "Parent","Parent","any_pointer",0,0,0,offsetof(Fruit,Parent),0,0,0,0,0,0,0,LA_UDF_REFER);
-    laAddSubGroup(pc, "container", "Container","Container of this fruit","bowl",0,LA_WIDGET_COLLECTION_SELECTOR,UIBowlSimple,offsetof(Fruit, Container),BowlGetFirst,0,laget_ListNext,0,0,0,0,LA_UDF_REFER);
-    laAddOperatorProperty(pc,"add","Add","Add a fruit into bundled", "FRUIT_add", '+', 0);
-    laAddOperatorProperty(pc,"remove","Remove","Remove a fruit", "FRUIT_remove", '-', 0);
-    laAddOperatorProperty(pc,"move","Move","Move a fruit", "FRUIT_move", '~', 0);
-
-    pcApple=pc=laAddPropertyContainer("apple", "Apple", "An apple", 0, UIApple, sizeof(Apple), 0, 0, 1);
-    laAddSubGroup(pc,"base", "Base","Base fruit","fruit",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_SINGLE|LA_UDF_LOCAL);
-    laAddFloatProperty(pc,"how_sweet","How Sweet","How sweet",0,0,0,100,0,0.1,0,0,offsetof(Apple,HowSweet),0,0,0,0,0,0,0,0,0,0,LA_AS_IDENTIFIER);
-
-    pcPear=pc=laAddPropertyContainer("pear", "Pear", "A pear", 0, UIPear, sizeof(Pear), 0, 0, 1);
-    laAddSubGroup(pc, "base","Base","Base fruit","fruit",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_SINGLE|LA_UDF_LOCAL);
-    p=laAddEnumProperty(pc,"really","Really","Really a pear?",LA_WIDGET_ENUM_CYCLE,0,0,0,0,offsetof(Pear,Really),0,0,0,0,0,0,0,0,0,LA_AS_IDENTIFIER);
-    laAddEnumItemAs(p,"NAH","Nah","Nah I don't think so", 0, L'🤔');
-    laAddEnumItemAs(p,"YEAH","Yeah","It's really a pear", 1, L'😄');
-    
-    laSaveProp("basket");
-
-    tnsObject* s=tnsCreateRootObject("My Root");
-    tnsObject* o=tnsCreateLight(s,"Sun",100,100,100,1,1);
-    tnsVector3d target={0,0,0}, up={0,0,1};
-    tnsLookAt(o,target,up);
-}
-
-//void la_DetachedPanel1(laPanel* p){
-//    la_MakeDetachedProp(p, "tns.texture_list", "tex");
-//}
-//void la_PanelHeader1(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *cr, int context){
-//    laColumn* c=cr?cr:laFirstColumn(uil);
-//    laShowItemFull(uil,c,DetachedProps,"tex", LA_WIDGET_COLLECTION_SELECTOR, 0 ,0,0);
-//}
-
 int main(int argc, char *argv[]){
-
     laGetReady();
 
-    RegisterEverything();
     RegisterCalculator();
 
     laRefreshUDFRegistries();
     laEnsureUserPreferences();
     
     laAddRootDBInst("calc");
-    laAddRootDBInst("basket");
 
     laWindow* w = laDesignWindow(-1,-1,600,600);
 
-    laLayout* l = laDesignLayout(w, "Second Layout");
+    laLayout* l = laDesignLayout(w, "Settings Layout");
     laBlock* b = l->FirstBlock;
     laPanel* p=laCreatePanel(b, "LAUI_user_preferences");
 
-    l = laDesignLayout(w, "First Layout");
-    b = l->FirstBlock;
-    p=laCreatePanel(b, "panel_fruit");
-
-    l = laDesignLayout(w, "Scene Layout");
-    b = l->FirstBlock;
-    p=laCreatePanel(b, "panel_scene");
-    
-    l = laDesignLayout(w, "Nodes Layout");
+    l = laDesignLayout(w, "Nodes list");
     b = l->FirstBlock;
-    p=laCreatePanel(b, "panel_nodes");
+    p=laCreatePanel(b, "panel_calc_help");
+    p=laCreatePanel(b, "panel_calc_regular");
+    p=laCreatePanel(b, "panel_calc");
 
     laStartWindow(w);
     laMainLoop();

+ 253 - 0
example_viewer.c

@@ -0,0 +1,253 @@
+#include "la_5.h"
+
+STRUCTURE(ExampleItem){
+    laListItem Item;
+    laSafeString* Name;
+    laSafeString* Path;
+    laSafeString* Code;
+    laUiDefineFunc Define;
+};
+
+STRUCTURE(ExampleViewer){
+    real pad;
+    laListHandle Examples;
+    ExampleItem* CurrentExample;
+    int ShowCode;
+};
+
+ExampleViewer* EV;
+
+void ExamplesList(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil),*cl,*cr; laSplitColumn(uil,c,0.4); cl=laLeftColumn(c,7); cr=laRightColumn(c,0);
+    laShowItemFull(uil,cl,0,"viewer.examples",0,0,laui_IdentifierOnly,0);
+    laUiItem* b=laOnConditionThat(uil,cr,laPropExpression(0,"viewer.current"));{
+        laShowItemFull(uil,cr,0,"viewer.examples",LA_WIDGET_COLLECTION_SINGLE,0,0,0)->Flags|=LA_UI_FLAGS_NO_DECAL;
+    }laElse(uil,b);{
+        laShowLabel(uil,cr,"Select an example to begin.",0,0);
+    }laEndCondition(uil,b);
+}
+
+static void AddExample(char* name, char* path, laUiDefineFunc func){
+    ExampleItem* ei=memAcquire(sizeof(ExampleItem));
+    strSafeSet(&ei->Name,name); strSafeSet(&ei->Path,path); ei->Define=func; strSafeSet(&ei->Code,"// Unable to locate source code for this example");
+    lstAppendItem(&EV->Examples,ei);
+    char filename[512]; sprintf(filename,"example_source_files/%s.c",path);
+    FILE* f=fopen(filename,"r"); if(!f) return;
+    int s; fseek(f,0,SEEK_END); s=ftell(f); fseek(f,0,SEEK_SET); if(!s){ fclose(f); return; }
+    char* buf=calloc(1,s); fread(buf,s,1,f); fclose(f);
+    strSafeSet(&ei->Code,buf);
+    free(buf);
+}
+
+static void ExamplesCleanUp(){
+    ExampleItem* ei;
+    while(ei=lstPopItem(&EV->Examples)){ strSafeDestroy(&ei->Name); strSafeDestroy(&ei->Path); strSafeDestroy(&ei->Code); memFree(ei); }
+    memFree(EV);
+}
+
+#define EXAMPLE_COMMON_BEGIN\
+    ExampleItem* ei=This->EndInstance;\
+    laColumn* c=laFirstColumn(uil);\
+    laUiItem* row=laBeginRow(uil,c,0,0);\
+    laShowItem(uil,c,This,"name")->Expand=1;\
+    laUiItem* eui=laShowItem(uil,c,0,"viewer.show_code");eui->Flags|=LA_UI_FLAGS_EXPAND;\
+    laEndRow(uil,row);\
+    laUiItem* bu=laOnConditionThat(uil,c,laPropExpression(0,"viewer.show_code"));{\
+        char instructions[256]; sprintf(instructions,"text=Open \"%s.c\" in external editor;file=%s.c",ei->Path->Ptr,ei->Path->Ptr);\
+        row=laBeginRow(uil,c,0,0); laShowSeparator(uil,c)->Expand=1; \
+        laShowItemFull(uil,c,0,"EXAMPLE_open_source_code",0,instructions,0,0);\
+        laEndRow(uil,row);\
+        laShowSeparator(uil,c);\
+        laShowItem(uil,c,This,"code")->Extra->HeightCoeff=-2;\
+    }laElse(uil,bu);{\
+        row=laBeginRow(uil,c,0,0);\
+        char instructions[256]; sprintf(instructions,"text=Launch \"%s\";program=%s",ei->Name->Ptr,ei->Path->Ptr);\
+        laShowItemFull(uil,c,0,"EXAMPLE_launch_program",0,instructions,0,0);\
+        laEndRow(uil,row);\
+        laShowSeparator(uil,c);
+
+#define EXAMPLE_COMMON_END\
+    }laEndCondition(uil,bu);
+
+void ui_Fruits(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    EXAMPLE_COMMON_BEGIN
+
+    laShowLabel(uil,c,"\
+This demo mainly shows three things:\n\
+    1) The dynamic-typed list manipulation, operators, and UI adaptation in LaGUI.\n\
+    2) The ability to save and load data files based on the data structure description (via PropContainer and Prop).\n\
+    3) Automatic undo-redo support based on data structure description.\n\
+\n\
+Play with this demo:\n\
+--------------------\n\
+    1) Click 'Stuff' and 'Bowl' can reveal things inside those list handles.\n\
+    2) Add or remove fruits or bowls with the UI.\n\
+    3) You can select bowl reference in the last select box (COLLECTION_SELECTOR) of each fruit.\n\
+    4) Try pressing Ctrl-Z/Ctrl-Shift-Z to undo and redo after you changed anything. In the console you should be able to see undo status.\n\
+    5) Try closing the window while you have bowls created, a dialog would show to ask you to save changes.\n\
+    6) Assign new files with the dropbox to the right, use file name ending with .udf to save changes.\n\
+    7) You can later read the files using File->Read.\
+        ",0,0)->Flags|=LA_TEXT_LINE_WRAP|LA_TEXT_MONO;
+    
+    EXAMPLE_COMMON_END
+}
+
+
+void ui_Simplest(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    EXAMPLE_COMMON_BEGIN
+
+    laColumn* c=laFirstColumn(uil);
+
+    laShowLabel(uil,c,"\
+Hello! Thanks for trying LaGUI!\n\
+===============================\n\
+    The first example is the simplest possible LaGUI application, we only create a panel saying \"Hello world!\".\
+        ",0,0)->Flags|=LA_TEXT_LINE_WRAP|LA_TEXT_MONO;
+
+    laUiItem* row=laBeginRow(uil,c,0,0);
+    laShowItemFull(uil,c,0,"EXAMPLE_launch_program",0,"program=simplest;text=Click to launch the program",0,0);
+    laEndRow(uil,row);
+
+    laShowLabel(uil,c,"\n\
+How do I begin?\n\
+---------------\n\
+    Press the 🞆 button on the top left to explore LaGUI's built-in functionalities. Every panel in a LaGUI program should be accessible from the \
+🞆 menu. Since this is the simplest program, you can only find built-in panels in there.\n\
+\n\
+To manipulate the layout:\n\
+-------------------------\n\
+    1) You can dock panels by pressing the 🗖 button, then drop the panel to approperiate region.\n\
+    2) To tear down the panel, drag the panel title to the center of each region and release.\n\
+    3) You could maximize a region by clicking ⯆ on the top left of each region and select approperiate operations.\n\
+    4) You can also use 🗗 button on the top to create a new window/layout, then dock some new panels there to customize your workspace.\n\
+        ",0,0)->Flags|=LA_TEXT_LINE_WRAP|LA_TEXT_MONO;
+
+    EXAMPLE_COMMON_END
+}
+void ui_Calculator(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    EXAMPLE_COMMON_BEGIN
+
+    laColumn* c=laFirstColumn(uil);
+
+    laShowLabel(uil,c,"\
+Calculator demo\n\
+===============\n\
+    This program mainly demonstrates how to use LaGUI to create a node-based UI. The program creates a simple node-socket graph structure for \
+the user to edit the data flow and the logic. The demo didn't implement the actual \"evaluation\" part, it's mainly to provide a reference to \
+how should one create such a data structure description in LaGUI's property system, and the special UI flags for enabling the node rack canvas.\n\
+        ",0,0)->Flags|=LA_TEXT_LINE_WRAP|LA_TEXT_MONO;
+
+    laShowLabel(uil,c,"\
+Play with calculator demo:\n\
+--------------------------\n\
+    1) Click \"New Calculator\" to create a calculator page.\n\
+    2) Click the \"+\" buttons to the left/right of the rack name to add a node rack.\n\
+    3) Click \"Add Node\" button to insert a node into the rack.\n\
+    4) Insert more nodes and connecct nodes with each other, you can create more racks too.\n\
+    5) Ctrl-Z and Ctrl-Shift-Z to undo and redo, you should be able to see connections change as how you manipulated them.\n\
+    6) Try closing the window while you have an caluclator, a dialog would show to ask you to save changes. You can save them too.\n\
+    7) If you try to read calculator files, the calculator page that's referenced by any \"Reference\" node will be automatically loaded too.\n\
+    8) The imlementation of a DAG evaluator is left for you as a practice.\n\
+    9) There are some node-related configurable options in user preferences, try changing those and see what those options do.\n\
+        ",0,0)->Flags|=LA_TEXT_LINE_WRAP|LA_TEXT_MONO;
+
+    EXAMPLE_COMMON_END
+}
+void ui_Modelling(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    EXAMPLE_COMMON_BEGIN
+
+    laColumn* c=laFirstColumn(uil);
+
+    laShowLabel(uil,c,"\
+Modelling functionality demo\n\
+============================\n\
+    LaGUI has a simple geometry layer that allows you to load or model mesh geometries. This example demonstrates the default modeller. \
+        ",0,0)->Flags|=LA_TEXT_LINE_WRAP|LA_TEXT_MONO;
+
+    EXAMPLE_COMMON_END
+}
+
+int inv_LaunchDemo(laOperator* a, laEvent* e){
+    char* program=strGetArgumentString(a->ExtraInstructionsP,"program"); if(!program) return LA_FINISHED;
+    char command[512]; sprintf(command,"./%s &",program);
+    system(command);
+    return LA_FINISHED;
+}
+int inv_OpenDemoSource(laOperator* a, laEvent* e){
+    char* file=strGetArgumentString(a->ExtraInstructionsP,"file"); if(!file) return LA_FINISHED;
+    char command[512]; sprintf(command,"xdg-open example_source_files/%s &",file);
+    system(command);
+    return LA_FINISHED;
+}
+
+
+laPropContainer *pcGeneric,*pcFruits,*pcSimplest,*pcModelling,*pcCalculator;
+
+void* get_ExampleViewer(void* unused){
+    return EV;
+}
+laPropContainer* get_ExamplesGetType(void* unused, ExampleItem* ei){
+    if(ei->Define==ui_Fruits) return pcFruits;
+    if(ei->Define==ui_Simplest) return pcSimplest;
+    if(ei->Define==ui_Modelling) return pcModelling;
+    if(ei->Define==ui_Calculator) return pcCalculator;
+    return pcGeneric;
+}
+void set_CurrentExample(ExampleViewer* v, ExampleItem* ei){
+    memAssignRef(v,&v->CurrentExample, ei); v->ShowCode=0;
+}
+
+#define EXAMPLE_ADD_PC(name,_PC,_UI)\
+    pc=laAddPropertyContainer("example_" name, name "Example", name "example",0,_UI,sizeof(ExampleItem),0,0,1); _PC=pc;\
+    laAddStringProperty(pc,"name","Name","Name of the example",LA_WIDGET_STRING_PLAIN,0,0,0,1,offsetof(ExampleItem,Name),0,0,0,0,LA_READ_ONLY|LA_AS_IDENTIFIER);\
+    laAddSubGroup(pc,"base","Base","Base example","program_example",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
+
+static void InitExamples(){
+    laCreateOperatorType("EXAMPLE_launch_program","Launch Program", "Launch example program",0,0,0,inv_LaunchDemo,0,L'🏃',0);
+    laCreateOperatorType("EXAMPLE_open_source_code","Open Source Code", "Open source code of the example in a external program",0,0,0,inv_OpenDemoSource,0,L'🡵',0);
+
+    laPropContainer* root=laDefineRoot(),*pc; laProp*p;
+    laAddSubGroup(root,"viewer","Viewer","Example viewer","example_viewer",0,0,0,-1,0,get_ExampleViewer,0,0,0,0,0,0);
+
+    pc=laAddPropertyContainer("example_viewer","Example Viewer","Example viewer root data",0,0,sizeof(ExampleViewer),0,0,1);
+    laAddSubGroup(pc,"examples","Examples","Example programs","program_example",get_ExamplesGetType,0,0,offsetof(ExampleViewer,CurrentExample),0,0,0,set_CurrentExample,0,0,offsetof(ExampleViewer,Examples),0);
+    laAddSubGroup(pc,"current","Current Example","Current example","program_example",get_ExamplesGetType,0,0,offsetof(ExampleViewer,CurrentExample),0,0,0,0,0,0,0,LA_UDF_REFER);
+    p=laAddEnumProperty(pc,"show_code","Show Code","Show code instead of descriptions",0,0,0,0,0,offsetof(ExampleViewer,ShowCode),0,0,0,0,0,0,0,0,0,0);
+    laAddEnumItemAs(p,"DESC","Description","Show description of the example",0,0);
+    laAddEnumItemAs(p,"IMPL","Code","Show implementation of the example",1,0);
+
+    pc=laAddPropertyContainer("program_example","Program Example","Program example",0,laui_IdentifierOnly,sizeof(ExampleItem),0,0,1); pcGeneric=pc;
+    laAddStringProperty(pc,"name","Name","Name of the example",LA_WIDGET_STRING_PLAIN,0,0,0,1,offsetof(ExampleItem,Name),0,0,0,0,LA_READ_ONLY|LA_AS_IDENTIFIER);
+    laAddStringProperty(pc,"code","Code","Code of the example",LA_WIDGET_STRING_MULTI,0,0,0,1,offsetof(ExampleItem,Code),0,0,0,0,LA_READ_ONLY);
+    
+    EXAMPLE_ADD_PC("simplest",pcSimplest,ui_Simplest);
+    EXAMPLE_ADD_PC("fruits",pcFruits,ui_Fruits);
+    EXAMPLE_ADD_PC("calculator",pcCalculator,ui_Calculator);
+    EXAMPLE_ADD_PC("modelling_main",pcModelling,ui_Modelling);
+
+    EV=memAcquire(sizeof(ExampleViewer));
+    AddExample("Simplest","simplest",ui_Simplest);
+    AddExample("Fruits","fruits",ui_Fruits);
+    AddExample("Calculator","calculator",ui_Calculator);
+    AddExample("Modelling","modelling_main",ui_Modelling);
+
+    laSetCleanupCallback(ExamplesCleanUp);
+}
+
+int main(int argc, char *argv[]){
+    laGetReady();
+
+    char buf[512];getcwd(buf,512);
+    printf("%s\n",buf);
+
+    InitExamples();
+
+    laRegisterUiTemplate("examples_list","Examples List", ExamplesList,0,0,"Demonstration");
+
+    laWindow* w = laDesignWindow(-1,-1,800,600);
+    laLayout* l = laDesignLayout(w,"LaGUI Examples");
+    laCreatePanel(l->FirstBlock,"examples_list");
+
+    laStartWindow(w);
+    laMainLoop();
+}

+ 319 - 0
fruits.c

@@ -0,0 +1,319 @@
+#include "la_5.h"
+
+extern LA MAIN;
+
+STRUCTURE(Bowl){
+    laListItem    Item;
+    laSafeString* Name;
+    laListHandle  Fruits;
+};
+STRUCTURE(Basket){
+    int pad;
+    int example;
+    laSafeString* name;
+    laListHandle Stuff;
+    laListHandle Bowls;
+};
+#define FRUIT_TYPE_APPLE 1
+#define FRUIT_TYPE_PEAR 2
+STRUCTURE(Fruit){
+    laListItem   Item;
+    int          Type;
+    Fruit*       WhyNot;
+    laListHandle*  Parent;
+    Bowl*        Container;
+    laListHandle Bundled;
+};
+STRUCTURE(Apple){
+    Fruit  Base;
+    real   HowSweet;
+};
+STRUCTURE(Pear){
+    Fruit Base;
+    int Really;
+};
+
+Basket B={0};
+
+laPropContainer* pcApple,*pcPear;
+laProp* pBasket, *pFruit, *pBowl;
+
+int INV_AddBowl(laOperator* a, laEvent* e){
+    Bowl* b=memAcquireHyper(sizeof(Bowl));
+    lstAppendItem(&B.Bowls,b);
+
+    laRecordAndPushProp(0, "basket");laNotifyUsers("basket");
+    laPrintDBInstInfo();
+
+    return LA_FINISHED;
+}
+int INV_RemoveBowl(laOperator* a, laEvent* e){
+    Bowl* b=a->This?a->This->EndInstance:B.Bowls.pLast; if(!b) return LA_FINISHED;
+    
+    lstRemoveItem(&B.Bowls, b);
+    memLeave(b);
+
+    laRecordAndPushProp(0, "basket");laNotifyUsers("basket");
+    laPrintDBInstInfo();
+
+    return LA_FINISHED;
+}
+int INV_AddFruit(laOperator* a, laEvent* e){
+    laListHandle* into=(a->This&&a->This->LastPs->p==pBowl)?&((Bowl*)a->This->EndInstance)->Fruits:0;
+    if(!into) into=a->This?&((Fruit*)a->This->EndInstance)->Bundled:0;
+
+    char* stype; int size=sizeof(Apple); int type=FRUIT_TYPE_APPLE;
+    if(stype=strGetArgumentString(a->ExtraInstructionsP,"type")){
+        if(!strcmp(stype,"pear")) {size=sizeof(Pear);type=FRUIT_TYPE_PEAR;}
+    }
+    Fruit* f=memAcquire(size); f->Type=type;
+    if(into){ lstAppendItem(into, f); f->Parent=into; laNotifyUsers("basket"); /*laNotifyUsersPPPath(a->This,"base.bundled");*/ }
+    else{ lstAppendItem(&B.Stuff, f); laNotifyUsers("basket");}
+
+    laRecordAndPushProp(0, "basket");
+    laPrintDBInstInfo();
+
+    return LA_FINISHED;
+}
+void DestroyFruit(Fruit* f){
+    Fruit* NextF;
+    for(Fruit*fi=f->Bundled.pFirst;fi;fi=NextF){
+        NextF=fi->Item.pNext;
+        DestroyFruit(fi);
+    }
+    memLeave(f);
+}
+int INV_RemoveFruit(laOperator* a, laEvent* e){
+    Fruit* f=a->This?a->This->EndInstance:0; laListHandle* l;
+    
+    if(f->Parent){ l=f->Parent;lstRemoveItem(l,f); laNotifyUsers("basket"); }
+    else{ l=&B.Stuff; lstRemoveItem(l,f);laNotifyUsers("basket"); }
+
+    DestroyFruit(f);
+
+    laRecordAndPushProp(0, "basket");
+
+    return LA_FINISHED;
+}
+int INV_MoveFruit(laOperator* a, laEvent* e){
+    Fruit* f=a->This?a->This->EndInstance:0; laListHandle* l; int direction=0; char* dir;
+    if(dir=strGetArgumentString(a->ExtraInstructionsP,"direction")){
+        if(!strcmp(dir,"up")) {direction=1;}
+    }
+    
+    if(f->Parent){ l=f->Parent; laNotifyUsers("basket"); /*laNotifyUsersPPPath(a->This,"base.parent.bundled");*/ }
+    else{ l=&B.Stuff; laNotifyUsers("basket"); }
+
+    if(direction) lstMoveUp(l,f); 
+    else lstMoveDown(l,f); 
+
+    laRecordAndPushProp(0, "basket");
+
+    return LA_FINISHED;
+}
+int INV_PushBasketState(laOperator* a, laEvent* e){
+    laRecordAndPushProp(0, "basket");
+    logPrint("Pushed state: \"basket\"\n");
+    return LA_FINISHED;
+}
+
+laPropContainer* FruitGetType(Fruit* f){
+    if(f->Type==FRUIT_TYPE_APPLE) return pcApple;
+    if(f->Type==FRUIT_TYPE_PEAR) return pcPear;
+    return pcApple;
+}
+
+void* FruitGetBasket(void* none){
+    return &B;
+}
+
+void* BowlGetFirst(void* none, void *UNUSED){
+    return B.Bowls.pFirst;
+}
+void* FruitGetFirst(void* none, void *UNUSED){
+    return B.Stuff.pFirst;
+}
+
+void FruitsPanel(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+    laColumn* cl;
+    laSplitColumn(uil,c,0.5);
+    cl=laLeftColumn(c,0);
+    
+    laShowItem(uil,c,0,"basket");
+    
+    laUiItem* r=laBeginRow(uil,c,0,0);
+    strSafeSet(&laShowItem(uil,c,0,"FRUIT_add")->ExtraInstructions,"type=apple;icon=🍏;text=Add Apple;");
+    strSafeSet(&laShowItem(uil,c,0,"FRUIT_add")->ExtraInstructions,"type=pear;icon=🍐;text=Add Pear;");
+    laShowItem(uil,c,0,"BOWL_add");
+    laEndRow(uil,r);
+    laShowSeparator(uil,c);
+
+    r=laBeginRow(uil,c,0,0);
+    laShowItem(uil,c,0,"LA_undo");
+    laShowItem(uil,c,0,"LA_redo");
+    laShowItem(uil,c,0,"STATE_push");
+    laEndRow(uil,r);    
+}
+void UIBowl(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+    laColumn* cl,*cr;
+    laSplitColumn(uil,c,0.4);
+    cl=laLeftColumn(c, 1);
+    cr=laRightColumn(c,0);
+    laUiItem*u;
+
+    laUiItem* r=laBeginRow(uil,cr,1,0);
+    laShowItem(uil,cr,This,"name")->Expand=1;
+    laShowItem(uil,cr,This,"remove")->Flags|=LA_UI_FLAGS_ICON;
+    laEndRow(uil,r);
+    
+    r=laOnConditionToggle(uil,cl,0,0,0,0,0);{
+        laShowItem(uil,cr,This,"fruits");
+        laUiItem* r1=laBeginRow(uil,cr,0,0);
+        strSafeSet(&laShowItem(uil,cr,This,"add_fruit")->ExtraInstructions,"type=apple;icon=🍏;text=Add Apple;");
+        strSafeSet(&laShowItem(uil,cr,This,"add_fruit")->ExtraInstructions,"type=pear;icon=🍐;text=Add Pear;");
+        laEndRow(uil,r1);
+    }laEndCondition(uil,r);
+}
+void UIApple(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+    laColumn* cl,*cr;
+    laSplitColumn(uil,c,0.4);
+    cl=laLeftColumn(c, 1);
+    cr=laRightColumn(c,0);
+    laUiItem*u;
+
+    laUiItem* r=laBeginRow(uil,cr,1,0);
+    laShowLabel(uil,cr,"Apple",0,0)->Expand=1;
+    laShowItem(uil,cr,This,"how_sweet")->Expand=1;
+    laShowItem(uil,cr,This,"base.why_not")->Expand=1;
+    laShowItem(uil,cr,This,"base.container")->Expand=1;
+    laShowItem(uil,cr,This,"base.remove")->Flags|=LA_UI_FLAGS_ICON;
+    u=laShowItem(uil,cr,This,"base.move"); strSafeSet(&u->ExtraInstructions,"direction=up;icon=🡹;");u->Flags|=LA_UI_FLAGS_ICON;
+    u=laShowItem(uil,cr,This,"base.move"); strSafeSet(&u->ExtraInstructions,"direction=down;icon=🡻;");u->Flags|=LA_UI_FLAGS_ICON;
+    laEndRow(uil,r);
+    r=laOnConditionToggle(uil,cl,0,0,0,0,0);{
+        laShowItem(uil,cr,This,"base.bundled");
+        laUiItem* r1=laBeginRow(uil,cr,0,0);
+        strSafeSet(&laShowItem(uil,cr,This,"base.add")->ExtraInstructions,"type=apple;icon=🍏;text=Add Apple;");
+        strSafeSet(&laShowItem(uil,cr,This,"base.add")->ExtraInstructions,"type=pear;icon=🍐;text=Add Pear;");
+        laEndRow(uil,r1);
+    }laEndCondition(uil,r);
+}
+void UIPear(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+    laColumn* cl,*cr;
+    laSplitColumn(uil,c,0.4);
+    cl=laLeftColumn(c, 1);
+    cr=laRightColumn(c,0);
+    laUiItem*u;
+
+    laUiItem* r=laBeginRow(uil,cr,1,0);
+    laShowLabel(uil,cr,"Pear",0,0)->Expand=1;
+    laShowItem(uil,cr,This,"really")->Expand=1;
+    laShowItem(uil,cr,This,"base.why_not")->Expand=1;
+    laShowItem(uil,cr,This,"base.container")->Expand=1;
+    laShowItem(uil,cr,This,"base.remove")->Flags|=LA_UI_FLAGS_ICON;
+    u=laShowItem(uil,cr,This,"base.move"); strSafeSet(&u->ExtraInstructions,"direction=up;icon=🡹;");u->Flags|=LA_UI_FLAGS_ICON;
+    u=laShowItem(uil,cr,This,"base.move"); strSafeSet(&u->ExtraInstructions,"direction=down;icon=🡻;");u->Flags|=LA_UI_FLAGS_ICON;
+    laEndRow(uil,r);
+    r=laOnConditionToggle(uil,cl,0,0,0,0,0);{
+        laShowItem(uil,cr,This,"base.bundled");
+        laUiItem* r1=laBeginRow(uil,cr,0,0);
+        strSafeSet(&laShowItem(uil,cr,This,"base.add")->ExtraInstructions,"type=apple;icon=🍏;text=Add Apple;");
+        strSafeSet(&laShowItem(uil,cr,This,"base.add")->ExtraInstructions,"type=pear;icon=🍐;text=Add Pear;");
+        laEndRow(uil,r1);
+    }laEndCondition(uil,r);
+}
+void UIBowlSimple(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+    laShowItemFull(uil,c,This,"name",LA_WIDGET_STRING_PLAIN, 0 ,0,0);
+}
+void UIFruitSimple(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+    laShowItem(uil,c,This,"type");
+}
+
+void ScenePanel(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+    
+    laUiItem* ui=laShowCanvas(uil,c,0,"tns.world",0,-1);
+    laDefault3DViewOverlay(ui);
+}
+
+int RegisterEverything(){
+    laRegisterUiTemplate("panel_fruit", "Fruits", FruitsPanel, 0, 0,0);
+
+    laCreateOperatorType("BOWL_add", "Add Bowl", "Add a bowl", 0,0,0,INV_AddBowl,0,'+',0);
+    laCreateOperatorType("BOWL_remove", "Remove Bowl", "Remove a bowl", 0,0,0,INV_RemoveBowl,0,'-',0);
+    laCreateOperatorType("FRUIT_add", "Add Fruit", "Add a fruit into the basket or bundle", 0,0,0,INV_AddFruit,0,'+',0);
+    laCreateOperatorType("FRUIT_remove", "Remove Fruit", "Remove a fruit", 0,0,0,INV_RemoveFruit,0,'-',0);
+    laCreateOperatorType("FRUIT_move", "Move Fruit", "Move a fruit", 0,0,0,INV_MoveFruit,0,'~',0);
+    laCreateOperatorType("STATE_push", "Push State", "Push basket state", 0,0,0,INV_PushBasketState,0,0,0);
+
+    laPropContainer* pc=laDefineRoot();
+    laAddSubGroup(pc, "basket", "Basket", "The basket", "basket",0,0,0,-1,FruitGetBasket,0,0,0,0,0,0,LA_UDF_SINGLE|LA_UDF_LOCAL);
+    laProp*p;
+    
+    pc=laAddPropertyContainer("bowl", "Bowl", "Some sort of a bowl", 0, UIBowl, sizeof(Bowl), 0, 0, 2);
+    laAddStringProperty(pc,"name","Name","Name of the bowl",0,0,0,0,1,offsetof(Bowl,Name),0,0,0,0,LA_AS_IDENTIFIER);
+    laAddSubGroup(pc, "fruits", "Fruits","Fruits","fruit",FruitGetType,0,0,-1,0,0,0,0,0,0,offsetof(Bowl,Fruits),0);
+    laAddOperatorProperty(pc,"remove","Remove","Remove a bowl", "BOWL_remove", '-', 0);
+    laAddOperatorProperty(pc,"add_fruit","Add","Add a fruit into the bowl", "FRUIT_add", '+', 0);
+
+    pc=laAddPropertyContainer("basket", "Basket", "A basket of fruits", 0, 0, sizeof(Basket), 0, 0, 1|LA_UDF_LOCAL|LA_PROP_OTHER_ALLOC|LA_UDF_SINGLE);
+    laAddIntProperty(pc,"example","Example","Example int",0,0,0,0,0,1,0,0,offsetof(Basket,example),0,0,0,0,0,0,0,0,0,0,0);
+    laAddStringProperty(pc,"name","Name","Name of the basket",0,0,0,0,1,offsetof(Basket,name),0,0,0,0,LA_UDF_REFER);
+    laAddSubGroup(pc, "stuff", "Stuff","Stuffs","fruit",FruitGetType,0,0,-1,0,0,0,0,0,0,offsetof(Basket,Stuff),0);
+    pBowl = laAddSubGroup(pc, "bowls", "Bowls","Bowls","bowl",0,0,0,-1,0,0,0,0,0,0,offsetof(Basket,Bowls),0);
+
+    pc=laAddPropertyContainer("fruit", "Fruit", "A fruit", 0, 0, sizeof(Fruit), 0, 0, 1);
+    p=laAddEnumProperty(pc,"type","Type","Type",0,0,0,0,0,offsetof(Fruit,Type),0,0,0,0,0,0,0,0,0,LA_AS_IDENTIFIER|LA_READ_ONLY);
+    laAddEnumItemAs(p,"APPLE","Apple","Apple", FRUIT_TYPE_APPLE, L'🍏');
+    laAddEnumItemAs(p,"PEAR","Pear","Pear", FRUIT_TYPE_PEAR, L'🍐');
+    pFruit = laAddSubGroup(pc, "bundled", "Bundled","Bundled","fruit",FruitGetType,0,0,-1,0,0,0,0,0,0,offsetof(Fruit,Bundled),0);
+    laAddSubGroup(pc, "why_not", "Why Not","Why not try","fruit",0,LA_WIDGET_COLLECTION_SELECTOR,UIFruitSimple,offsetof(Fruit,WhyNot),FruitGetFirst,0,laget_ListNext,0,0,0,0,LA_UDF_REFER);
+    laAddSubGroup(pc, "parent", "Parent","Parent","any_pointer",0,0,0,offsetof(Fruit,Parent),0,0,0,0,0,0,0,LA_UDF_REFER);
+    laAddSubGroup(pc, "container", "Container","Container of this fruit","bowl",0,LA_WIDGET_COLLECTION_SELECTOR,UIBowlSimple,offsetof(Fruit, Container),BowlGetFirst,0,laget_ListNext,0,0,0,0,LA_UDF_REFER);
+    laAddOperatorProperty(pc,"add","Add","Add a fruit into bundled", "FRUIT_add", '+', 0);
+    laAddOperatorProperty(pc,"remove","Remove","Remove a fruit", "FRUIT_remove", '-', 0);
+    laAddOperatorProperty(pc,"move","Move","Move a fruit", "FRUIT_move", '~', 0);
+
+    pcApple=pc=laAddPropertyContainer("apple", "Apple", "An apple", 0, UIApple, sizeof(Apple), 0, 0, 1);
+    laAddSubGroup(pc,"base", "Base","Base fruit","fruit",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_SINGLE|LA_UDF_LOCAL);
+    laAddFloatProperty(pc,"how_sweet","How Sweet","How sweet",0,0,0,100,0,0.1,0,0,offsetof(Apple,HowSweet),0,0,0,0,0,0,0,0,0,0,LA_AS_IDENTIFIER);
+
+    pcPear=pc=laAddPropertyContainer("pear", "Pear", "A pear", 0, UIPear, sizeof(Pear), 0, 0, 1);
+    laAddSubGroup(pc, "base","Base","Base fruit","fruit",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_SINGLE|LA_UDF_LOCAL);
+    p=laAddEnumProperty(pc,"really","Really","Really a pear?",LA_WIDGET_ENUM_CYCLE,0,0,0,0,offsetof(Pear,Really),0,0,0,0,0,0,0,0,0,LA_AS_IDENTIFIER);
+    laAddEnumItemAs(p,"NAH","Nah","Nah I don't think so", 0, L'🤔');
+    laAddEnumItemAs(p,"YEAH","Yeah","It's really a pear", 1, L'😄');
+    
+    laSaveProp("basket");
+
+    tnsObject* s=tnsCreateRootObject("My Root");
+    tnsObject* o=tnsCreateLight(s,"Sun",100,100,100,1,1);
+    tnsVector3d target={0,0,0}, up={0,0,1};
+    tnsLookAt(o,target,up);
+}
+
+int main(int argc, char *argv[]){
+    laGetReady();
+
+    RegisterEverything();
+
+    laRefreshUDFRegistries();
+    laEnsureUserPreferences();
+    
+    laAddRootDBInst("basket");
+
+    laWindow* w = laDesignWindow(-1,-1,600,600);
+
+    laLayout* l = laDesignLayout(w, "Fruit Layout");
+    laBlock* b = l->FirstBlock;
+    laPanel* p=laCreatePanel(b, "panel_fruit");
+
+    laStartWindow(w);
+    laMainLoop();
+}

+ 1 - 4
modelling_main.c

@@ -31,11 +31,8 @@ int RegisterEverything(){
     tnsObject* mo=tnsCreateMeshPlane(s,"Plane", 0,0,0, 10);
     //tnsMeshEnterEditMode(mo);
     //tnsMeshLeaveEditMode(mo);
-//
-    //tnsCreateMeshPlane(s,"Plane", 0,0,10, 10);
 
-    //tnsMeshEnterEditMode(mo);
-    //tnsMeshLeaveEditMode(mo);
+    //tnsCreateMeshPlane(s,"Plane", 0,0,10, 10);
 }
 
 int main(int argc, char *argv[]){

+ 20 - 0
simplest.c

@@ -0,0 +1,20 @@
+#include "la_5.h"
+
+
+void MyPanel(laUiList *uil, laPropPack *This, laPropPack *DetachedProps, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+    laShowLabel(uil,c,"Hello world!",0,0);
+}
+
+int main(int argc, char *argv[]){
+    laGetReady();
+
+    laRegisterUiTemplate("my_panel","My Panel", MyPanel,0,0,"Demonstration");
+
+    laWindow* w = laDesignWindow(-1,-1,600,600);
+    laLayout* l = laDesignLayout(w,"My Layout");
+    laCreatePanel(l->FirstBlock,"my_panel");
+
+    laStartWindow(w);
+    laMainLoop();
+}