*/}}
Browse Source

Can r/w/undo/redo mappings

Can free differences from old side (some problems still)

socket undo redo not triggering redraw and also need to have an extra type
Yiming Wu 2 years ago
parent
commit
9eb8441f73

+ 31 - 15
source/lagui/la_data.c

@@ -624,8 +624,8 @@ laPropContainer *laAddPropertyContainer(const char *Identifier, const char *Name
 
     return pc;
 }
-void laPropContainerExtraFunctions(laPropContainer* pc, laContainerBeforeFreeF BeforeFree, laContainerUndoTouchedF Touched, laContainerpUDFPropagateF UDFPropagate){
-    pc->BeforeFree=BeforeFree; pc->UndoTouched=Touched; pc->UDFPropagate=UDFPropagate;
+void laPropContainerExtraFunctions(laPropContainer* pc, laContainerBeforeFreeF BeforeFree, laContainerUndoTouchedF Touched, laContainerpUDFPropagateF UDFPropagate, laUiDefineFunc MenuUi){
+    pc->BeforeFree=BeforeFree; pc->UndoTouched=Touched; pc->UDFPropagate=UDFPropagate; pc->MenuUiDefine=MenuUi;
     if(!pc->OtherAlloc && UDFPropagate){
         laAddOperatorProperty(pc,"__udf_propagate","Propagate","Propagate UDF to all child nodes", "LA_udf_propagate", 0,0);
     }
@@ -1045,7 +1045,8 @@ laUiDefineFunc* laGetPropertyUiDefine(laPropPack* pp, void* instance){
 }
 
 laPropContainer* la_EnsureSubTarget(laSubProp* sp, void* optional_instance){
-    if(optional_instance&&sp->GetType) return sp->GetType(optional_instance);
+    if(sp->Base.PropertyType!=LA_PROP_SUB){return sp->Base.SubProp;}
+    if(optional_instance && sp->GetType) return sp->GetType(optional_instance);
     if(sp->Base.SubProp){ return sp->Base.SubProp; }
     if(sp->TargetID)sp->Base.SubProp=la_ContainerLookup(sp->TargetID); return sp->Base.SubProp;
 }
@@ -3118,7 +3119,7 @@ int la_ExtractProp(laUDF *udf, laManagedUDF* mUDF, laPropPack *pp, void *ParentI
                     if (udf){
                         RealSize = RealSize ? RealSize : p->SubProp->NodeSize;
                         if (!Parent && !IsExceptionNode){
-                            if (p->UDFIsSingle && p->UDFNoCreate){ Instance = pp->EndInstance; }
+                            if (p->UDFIsSingle && pp->EndInstance){ Instance = pp->EndInstance; }
                             else{
                                 // if overwrite, find the instance here for hyper2, if not hyper 2 then notice can't overwrite.
                                 if (pc->Hyper == 2){
@@ -3725,6 +3726,7 @@ void la_FreeDBProp(laDBProp* dbp){
     }elif(dbp->p->PropertyType==LA_PROP_RAW){
         free(dbp->Data);
     }else{
+        //printf("-data- %x\n",dbp->Data);
         memFree(dbp->Data);
     }
     memFree(dbp);
@@ -3736,21 +3738,23 @@ void la_FreeDBInst(laDBInst* dbi, int no_freeinst){
     if(dbi->OriginalInstance) la_FreeInstance(dbi->OriginalInstance, dbi->pc, no_freeinst);
     memFree(dbi);
 }
-void la_FreeDiffCommand(laDiffCommand* dc, laDiff* d){
+void la_FreeDiffCommand(laDiffCommand* dc, laDiff* d, int FromLeft){
     printf("freedc %s\n",dc->p->Identifier);
     if(dc->p->PropertyType==LA_PROP_SUB && (((laSubProp*)dc->p)->ListHandleOffset||dc->p->UDFNoCreate||dc->p->UDFIsSingle) && (!dc->p->UDFIsRefer)){
         laDiffCommandInst* dci; laDiffCommandSub* dcs=dc;
-        while(dci=lstPopItem(&dcs->AddedInst)){ la_FreeDBInst(dci->DBInst,(dc->p->UDFNoCreate||(!dc->p->OffsetIsPointer))); memFree(dci); }
+        while(dci=lstPopItem(&dcs->AddedInst)){ if(!FromLeft) la_FreeDBInst(dci->DBInst,(dc->p->UDFNoCreate||(!dc->p->OffsetIsPointer))); memFree(dci); }
         while(dci=lstPopItem(&dcs->MovedInst)){ memFree(dci); }
-        while(dci=lstPopItem(&dcs->RemovedInst)){ dci->DBInst->DeletedDiff = 0; memFree(dci); }
+        while(dci=lstPopItem(&dcs->RemovedInst)){ if(FromLeft) la_FreeDBInst(dci->DBInst,(dc->p->UDFNoCreate||(!dc->p->OffsetIsPointer))); memFree(dci); }
     }elif(dc->p->PropertyType==LA_PROP_STRING && ((laStringProp*)dc->p)->IsSafeString){
         strSafeSet(&dc->Data,0);
+    }elif(dc->p->PropertyType==LA_PROP_RAW){
+        free(dc->Data);
     }
     memFree(dc);
 }
-void la_FreeDifference(laDiff* d){
+void la_FreeDifference(laDiff* d, int FromLeft){
     laDiffCommand* dc; laDiffExtraTouched* det;
-    while(dc=lstPopItem(&d->Commands)){ la_FreeDiffCommand(dc,d); }
+    while(dc=lstPopItem(&d->Commands)){ la_FreeDiffCommand(dc,d,FromLeft); }
     while(det=lstPopItem(&d->ExtraTouched)){ memFree(det); }
     if(d->Description&&d->Description->Ptr){printf("%s\n",d->Description->Ptr);}
     strSafeDestroy(&d->Description);
@@ -3761,18 +3765,30 @@ void la_FreeNewerDifferences(){
     for(laDiff* d=MAIN.Differences.pLast;d;d=PrevD){
         PrevD=d->Item.pPrev;
         lstRemoveItem(&MAIN.Differences,d);
-        la_FreeDifference(d);
+        la_FreeDifference(d,0);
         if(MAIN.HeadDifference==d){ MAIN.HeadDifference=PrevD; laPushDifferenceOnly(0,0); break; }
     }
 }
+void la_FreeOlderDifferences(int KeepHowMany){
+    laDiff* endd; int count=0;
+    for(endd=MAIN.HeadDifference;endd;endd=endd->Item.pPrev){
+        if(count>=KeepHowMany) break; count++; 
+    }
+    if(!endd) return; laDiff* d,*NextD;
+    while(d=lstPopItem(&MAIN.Differences)){
+        NextD=MAIN.Differences.pFirst;
+        la_FreeDifference(d,1);
+        if(MAIN.HeadDifference==d){ MAIN.HeadDifference=NextD; laPushDifferenceOnly(0,0); break; }
+        if(d==endd){ break; }
+    }
+}
 void la_FreeAllDifferences(){
-    MAIN.HeadDifference=MAIN.Differences.pFirst;
     la_FreeNewerDifferences();
+    la_FreeOlderDifferences(1); //XXX 0 seems to have problems
 }
 void la_NoLongerRecordUndo(){
     la_FreeAllDifferences();
-    // XXX we need to have la_FreeOlderDifferences() before this that way the DBInst tree is consistent and then we won't have double free.
-    //laDBProp*dbp; while(dbp=lstPopItem(&MAIN.RootDBInst.Props)){ la_FreeDBProp(dbp); }
+    laDBProp*dbp; while(dbp=lstPopItem(&MAIN.RootDBInst.Props)){ la_FreeDBProp(dbp); }
     laDBRecordedProp* p; while(p=lstPopItem(&MAIN.DBRecordedProps)){ strSafeDestroy(&p->OriginalPath); memFree(p); }
     hshFree(&MAIN.DBInstLink);
 }
@@ -3786,7 +3802,7 @@ void la_RelinkDBInst(laDBInst* dbi, void* New_OriginalInstance){
 }
 laDBInst* laAddDBInst(laDBProp* parent, void* inst, laPropContainer* pc, laDiff* Begin){
     laDBInst* dbi=memAcquire(sizeof(laDBInst)); if(!Begin) Begin=MAIN.HeadDifference;
-    dbi->pc=pc; dbi->CreatedDiff = Begin;
+    dbi->pc=pc;
     if(parent){ lstAppendItem(&((laDBSubProp*)parent)->Instances, dbi); }
     la_RelinkDBInst(dbi, inst);
     return dbi;
@@ -4103,7 +4119,7 @@ void la_RestoreRawDBProp(laDBRawProp* dbp, laDiffCommandRaw* dc){
 }
 
 void la_AddUndoPostNode(laDBInst* dbi){
-    if(!dbi->pc->UndoTouched) return;
+    if((!dbi->pc) || (!dbi->pc->UndoTouched)) return;
     for(laDiffPost* dp=MAIN.DiffTouched.pLast;dp;dp=dp->Item.pPrev){ if(dp->instance==dbi->OriginalInstance&&dp->Touched==dbi->pc->UndoTouched) return; }
     laDiffPost* dp=lstAppendPointerSized(&MAIN.DiffTouched, dbi->OriginalInstance, sizeof(laDiffPost));
     dp->Touched=dbi->pc->UndoTouched;

+ 2 - 3
source/lagui/la_data.h

@@ -127,6 +127,7 @@ STRUCTURE(laPropContainer){
     laListHandle Props;
 
     laUiDefineFunc UiDefine;
+    laUiDefineFunc MenuUiDefine;
 
     laListHandle FailedNodes;
     laListHandle TrashBin;
@@ -561,8 +562,6 @@ STRUCTURE(laDBInst){
     void* OriginalInstance;
     laPropContainer* pc;
     laListHandle Props;
-    laDiff* CreatedDiff;
-    laDiff* DeletedDiff;
 };
 STRUCTURE(laDBProp){
     laListItem Item;
@@ -744,7 +743,7 @@ void la_FreePropertyContainer(laPropContainer* pc);
 
 laPropContainer *laAddPropertyContainer(const char *Identifier, const char *Name, const char *Description, uint32_t IconID, laUiDefineFunc DefaultUiDefine,
                                         int NodeSize, laContainerPostReadFunc PostRead, laContainerPostReadFunc PostReadIm, int IsHyper);
-void laPropContainerExtraFunctions(laPropContainer* pc, laContainerBeforeFreeF BeforeFree, laContainerUndoTouchedF UndoTouched, laContainerpUDFPropagateF UDFPropagate);
+void laPropContainerExtraFunctions(laPropContainer* pc, laContainerBeforeFreeF BeforeFree, laContainerUndoTouchedF UndoTouched, laContainerpUDFPropagateF UDFPropagate, laUiDefineFunc MenuUi);
 const char *la_GetPropertyTypeString(int Type);
 
 NEED_STRUCTURE(laWidget);

+ 12 - 10
source/lagui/la_interface.h

@@ -221,6 +221,13 @@ STRUCTURE(laNodeInSocket){
 NEED_STRUCTURE(laBaseNodeType);
 NEED_STRUCTURE(laRackPage);
 
+STRUCTURE(laRackPageCollection){
+    laRackPage* CurrentPage;
+    laListHandle Pages;
+    laListHandle Eval;
+    int NeedEval,NeedRebuild;
+};
+
 typedef laBaseNodeType* (*laGetBaseNodeTypeF)(char* str);
 
 STRUCTURE(LA){
@@ -396,15 +403,8 @@ STRUCTURE(LA){
 
     laListHandle MediaFiles;
 
-    laRackPage* CurrentInputMappingPage;
-    laListHandle InputMappingPages;
-    laListHandle InputMappingEval;
-    int MappingNeedEval,MappingNeedRebuild;
-
-    laRackPage* CurrentDriverPage;
-    laListHandle DriverPages;
-    laListHandle DriverEval;
-    int DriverNeedEval,DriverNeedRebuild;
+    laRackPageCollection* InputMapping;
+    laRackPageCollection* Drivers;
 
     laBaseNodeType** NodeTypes; int NodeTypeNext, NodeTypeMax;
     laUiDefineFunc ExtraAddInputNodes;
@@ -1293,6 +1293,7 @@ STRUCTURE(laNodeRack){
     laListItem Item;
     laSafeString* Name;
     laListHandle Nodes;
+    laRackPage* ParentPage;
     int RackType;
 };
 NEED_STRUCTURE(laInputControllerNode);
@@ -1392,6 +1393,7 @@ void laui_IntPropInfo(laUiList *uil, laPropPack *Base, laPropPack *UNUSED_This,
 void laui_FloatPropInfo(laUiList *uil, laPropPack *Base, laPropPack *UNUSED_This, laColumn *ExtraColumns, int context);
 
 void laui_DefaultPropDetails(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context);
+void laui_DefaultNodeOperationsPropUiDefine(laUiList *uil, laPropPack *This, laPropPack *OperatorProps, laColumn *UNUSED, int context);
 void laui_DefaultPropUiDefine(laUiList *uil, laPropPack *This, laPropPack *OperatorProps, laColumn *UNUSED, int context);
 void laui_StringPropUiDefine(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context);
 void laui_IdentifierOnly(laUiList *uil, laPropPack *This, laPropPack *OP_UNUSED, laColumn *Extra, int context);
@@ -1563,7 +1565,7 @@ void laEnsurePanelInBound(laPanel *p,laUiList* uil);
 int laEnclosePanelContent(laPanel *p, laUiList *uil);
 laPanel *laEnableIdlePanel(laPanel *Attachment, laOperator *a, laPropPack *OperatorProps, laUiDefineFunc ReplaceUiDefine, laPropPack *This,
                                int L, int R, int B, int MaxGH, int MaxW, laEvent *e);
-laPanel *laEnablePropertyPanel(laPanel *Attachment, laOperator *a, laPropPack *OperatorProps, laUiDefineFunc ReplaceUiDefine, laPropPack *This,
+laPanel *laEnablePropertyPanel(laPanel *Attachment, laOperator *a, laPropPack *OperatorProps, laUiDefineFunc ReplaceUiDefine, laUiDefineFunc FallBackUiDefine, laPropPack *This,
                                int L, int R, int B, int MaxGH, int MaxW, laEvent *e);
 laPanel *laEnableEmptyPropertyPanel(laPanel *Attachment, laOperator *a, int L, int R, int U, int MaxGH, laEvent *e);
 laPanel *laEnableMenuPanel(laPanel *Attachment, laOperator *a, laUiList *MenuRefer, laPropPack *This,

+ 39 - 60
source/lagui/la_kernel.c

@@ -337,6 +337,9 @@ int laGetReady(){
 
     arrEnsureLength(&MAIN.NodeTypes,0,&MAIN.NodeTypeMax,sizeof(laBaseNode*));
 
+    MAIN.InputMapping=memAcquire(sizeof(laRackPageCollection));
+    MAIN.Drivers=memAcquire(sizeof(laRackPageCollection));
+
     //XDestroyWindow(MAIN.dpy, win);
 
     MAIN.ColorAccessCorrectGamma = 1;
@@ -397,7 +400,7 @@ int laGetReady(){
 
     la_RegenerateWireColors();
 
-    laSaveProp("la.input_mapping_pages");
+    laSaveProp("la.input_mapping");
 
     logPrint("Initialization Completed\n");
 }
@@ -406,7 +409,7 @@ void laShutoff(){
     strSafeDestroy(&MAIN.WorkingDirectory);
     strSafeDestroy(&MAIN.example_string);
 
-    //la_NoLongerRecordUndo(); XXX: it's not reliable yet, we need to only remove steps that are consistent with current data structure
+    la_NoLongerRecordUndo(); //XXX: it's not reliable yet, we need to only remove steps that are consistent with current data structure
     // (Must undo/redo to desired position or delete from back/front without moving target head. This can be done but this is of lower priority. )
 
     laWindow* wi; while(wi=lstPopItem(&MAIN.Windows)){ la_DestroyWindow(wi); }
@@ -453,6 +456,7 @@ void laEnsureUserPreferences(){
     laUDFRegistry* r=laFindUDFRegistry(path);
     if(!r){ laSaveUserPreferences(); return; }
     laUDF* udf=laOpenUDF(r->Path->Ptr,1,0,0); if(!udf){ logPrint("Can't read preferences. Using default settings."); return; }
+    laClearUDFRegistries(); while(MAIN.ResourceFolders.pFirst){ laRemoveResourceFolder(MAIN.ResourceFolders.pFirst); }
     laExtractUDF(udf,0,LA_UDF_MODE_OVERWRITE,0);
     laCloseUDF(udf);
 }
@@ -2267,10 +2271,10 @@ laPanel *laEnableIdlePanel(laPanel *Attachment, laOperator *a, laPropPack *Opera
 
     return p;
 }
-laPanel *laEnablePropertyPanel(laPanel *Attachment, laOperator *a, laPropPack *OperatorProps, laUiDefineFunc ReplaceUiDefine, laPropPack *This,
+laPanel *laEnablePropertyPanel(laPanel *Attachment, laOperator *a, laPropPack *OperatorProps, laUiDefineFunc ReplaceUiDefine, laUiDefineFunc FallBackUiDefine, laPropPack *This,
                                int L, int R, int B, int MaxGH, int MaxW, laEvent *e){
     laOperator *ai = a;
-    laPanel *p;
+    laPanel *p; laPropContainer* sub;
     int GX, GY, GW, t = 0;
     int b;
     laUiDefineFunc def = ReplaceUiDefine;
@@ -2278,15 +2282,11 @@ laPanel *laEnablePropertyPanel(laPanel *Attachment, laOperator *a, laPropPack *O
 
     if (!def){
         if (This && This->LastPs->p->UiDefine) def = This->LastPs->p->UiDefine;
-        if (!def && This->LastPs->p->SubProp && This->LastPs->p->SubProp->UiDefine) def=This->LastPs->p->SubProp->UiDefine;
-        else def = laui_DefaultPropUiDefine;
+        if ((!def) && (sub=la_EnsureSubTarget(This->LastPs->p,This->EndInstance)) && sub->MenuUiDefine) def=sub->MenuUiDefine;
+        else def = FallBackUiDefine?FallBackUiDefine:laui_DefaultPropUiDefine;
     }
 
-    //laLocalToWindow(0,Attachment, &L, &t);
-    //laLocalToWindow(0,Attachment, &R, &B);
-
-    GX = L;
-    GY = B;
+    GX = L; GY = B;
     GW = (R - L) > MaxW ? MaxW : (R - L);
 
     p = laDesignPropPanel("TMP", GX, GY, GW, MaxGH, def, This, OperatorProps);
@@ -3847,7 +3847,7 @@ void la_DestroyUiList(laUiList *uil, int NoFree, int RemoveUsers, int OnlyRemove
     if (!NoFree) memFree(uil);
 }
 void la_DestroyTabPage(laUiItem *ui, laUiList *Tab, int RemoveUsers){
-    lstRemoveItem(&ui->Subs, Tab);
+    if(ui) lstRemoveItem(&ui->Subs, Tab);
     la_DestroyUiList(Tab, 1, RemoveUsers, 0);
 }
 void la_CreateUiAfter(laUiList *uil, laUiItem *after, laUiDefineFunc Define, laPropPack *Base, laPropPack *This, laColumn **ExtraColums){
@@ -4067,19 +4067,22 @@ int la_CalculateRowExpand(laRowInfo* ri, laUiItem* ui_end, int WaitAnimation){
 void la_RecordSocketRuntimePosition(laUiItem* ui){
     laProp* p=ui->PP.LastPs->p; laPropContainer* pc=la_EnsureSubTarget(p,0);
     int sl,sr,su,sb;
-    if(ui->Flags&LA_UI_SOCKET_LABEL_N){ sl=ui->L; sr=ui->R; su=ui->U+LA_RH; }
-    elif(ui->Flags&LA_UI_SOCKET_LABEL_S){ sl=ui->L; sr=ui->R; su=ui->U; }
-    elif(ui->Flags&LA_UI_SOCKET_LABEL_W){ sl=ui->L+LA_2RH; sr=ui->R; su=ui->U; }
-    elif(ui->Flags&LA_UI_SOCKET_LABEL_E){ sl=ui->L; sr=ui->R-LA_2RH; su=ui->U; }
-    else{ sl=ui->L; su=ui->U; sr=ui->R; } sb=su+LA_RH; 
+    if(ui->Flags&LA_UI_SOCKET_LABEL_N){ sl=ui->TL; sr=ui->TR; su=ui->TU+LA_RH; }
+    elif(ui->Flags&LA_UI_SOCKET_LABEL_S){ sl=ui->TL; sr=ui->TR; su=ui->TU; }
+    elif(ui->Flags&LA_UI_SOCKET_LABEL_W){ sl=ui->TL+LA_2RH; sr=ui->TR; su=ui->TU; }
+    elif(ui->Flags&LA_UI_SOCKET_LABEL_E){ sl=ui->TL; sr=ui->TR-LA_2RH; su=ui->TU; }
+    else{ sl=ui->TL; su=ui->TU; sr=ui->TR; } sb=su+LA_RH; 
     if(pc==LA_PC_SOCKET_OUT){
         laNodeOutSocket* s=ui->PP.EndInstance; s->RuntimeX=(sl+sr)/2; s->RuntimeY=(su+sb)/2;
     }else{
         laNodeInSocket* s=ui->PP.EndInstance;  s->RuntimeX=(sl+sr)/2; s->RuntimeY=(su+sb)/2;
     }
 }
+laUiList* la_GiveExistingPage(laListHandle* from, void* instance){
+    for(laUiList* uil=from->pFirst;uil;uil=uil->Item.pNext){ if(uil->Instance == instance){ lstRemoveItem(from,uil); return uil; } }
+}
 int la_UpdateUiListRecursive(laUiList *uil, int U, int L, int R, int B, int Fast, laPanel *ParentPanel){
-    laUiItem *ui;
+    laUiItem *ui; laListHandle TempPages={0}; laUiList* FoundUil;
     laBoxedTheme *bt;
     int Lowest = 0;
     int HyperValue = 0;
@@ -4286,53 +4289,32 @@ int la_UpdateUiListRecursive(laUiList *uil, int U, int L, int R, int B, int Fast
                         ER = tnsInterpolate(ui->TL, ui->TR, (Col + 1) * ElementWidth);
                     }
 
-                    if (!iuil){
-                        la_AddInstancePage(ui, TInstance, 0);
-                        la_CalcUiTopInfluence(&uil->Columns, ui);
-                        if (Template) laMakeUiListFromTemplate(ui->Page, Template, &ParentPanel->PP, &ParentPanel->PropLinkPP, &ui->PP, 0, &uil->Columns, ui->TemplateContext);
-                        SubB = la_UpdateUiListRecursive(ui->Page, Gap+Begin+(NoDecal?0:bt->TM), EL+(NoDecal?0:bt->LM), ER-(NoDecal?0:bt->RM), B, Fast, ParentPanel) + bt->TM;
-                        ElementB = RowPriority ? (SubB > ElementB ? SubB : ElementB) : SubB;
-
-                        iuil = ui->Page->Item.pNext;
-                    }
-
                     while (iuil && iuil->Instance != TInstance){
-                        while (iuil && iuil->Instance != TInstance){
-                            puil = iuil->Item.pNext;
-                            la_DestroyTabPage(ui, iuil, 0);
-                            iuil = puil;
+                        if(FoundUil=la_GiveExistingPage(&TempPages, TInstance)){
+                            lstInsertItemBefore(&ui->Subs, FoundUil, iuil); puil = FoundUil;
+                        }else{
+                            puil = iuil->Item.pNext; lstRemoveItem(&ui->Subs, iuil); lstAppendItem(&TempPages, iuil);
                         }
-                        if (!iuil){
+                        iuil = puil;
+                    }
+                    if(!iuil){
+                        if(FoundUil=la_GiveExistingPage(&TempPages, TInstance)){ lstAppendItem(&ui->Subs, FoundUil); iuil = FoundUil; }
+                    }
+                    if (!iuil){
                             la_AddInstancePage(ui, TInstance, 0);
                             la_CalcUiTopInfluence(&uil->Columns, ui);
                             if (Template) laMakeUiListFromTemplate(ui->Page, Template, &ParentPanel->PP, &ParentPanel->PropLinkPP, &ui->PP, 0, &uil->Columns, ui->TemplateContext);
                             SubB = la_UpdateUiListRecursive(ui->Page, Gap+Begin+(NoDecal?0:bt->TM), EL+(NoDecal?0:bt->LM), ER-(NoDecal?0:bt->RM), B, Fast, ParentPanel) + bt->TM;
                             ElementB = RowPriority ? (SubB > ElementB ? SubB : ElementB) : SubB;
-
                             iuil = ui->Page->Item.pNext;
-                            break;
-                        }
-                    }
-
-                    if (iuil && iuil->Instance == TInstance){
+                    }else{
                         la_CalcUiTopInfluence(&uil->Columns, ui);
                         SubB = la_UpdateUiListRecursive(iuil, Gap+Begin+(NoDecal?0:bt->TM), EL+(NoDecal?0:bt->LM), ER-(NoDecal?0:bt->RM), B, Fast, ParentPanel) + bt->TM;
                         ElementB = RowPriority ? (SubB > ElementB ? SubB : ElementB) : SubB;
-
                         la_CalcUiItemInfluence(&uil->Columns, ui);
                         iuil = iuil->Item.pNext;
-                        //TInstance = laGetNextInstance(ui->PP.LastPs->p, TInstance, &pi);
-                        //ui->PP.EndInstance = TInstance;
-                        if (!TInstance){
-                            while (iuil){
-                                puil = iuil->Item.pNext;
-                                la_DestroyTabPage(ui, iuil, 0);
-                                iuil = puil;
-                            }
-                            break;
-                        }
-                        //continue;
                     }
+
                     TInstance = laGetNextInstance(ui->PP.LastPs->p, TInstance, &pi);
                     Template = ui->Template?ui->Template:laGetPropertyUiDefine(&ui->PP, TInstance);
                     ui->PP.EndInstance = TInstance;
@@ -4361,11 +4343,8 @@ int la_UpdateUiListRecursive(laUiList *uil, int U, int L, int R, int B, int Fast
                     ui->TB = ElementB;
                     MaxB = MaxB < ElementB ? ElementB : MaxB;
                 }
-                while (iuil){
-                    puil = iuil->Item.pNext;
-                    la_DestroyTabPage(ui, iuil, 0);
-                    iuil = puil;
-                }
+                while (iuil){ puil = iuil->Item.pNext; la_DestroyTabPage(ui, iuil, 0); iuil = puil; }
+                while (iuil=lstPopItem(&TempPages)){ la_DestroyTabPage(0, iuil, 0); }
                 ui->PP.EndInstance = laGetActiveInstance(ui->PP.LastPs->p, ui->PP.LastPs->UseInstance, &pi);
                 ui->TB = MaxB;
                 if(Spread){ ui->TR=ER-Spread*LA_RH; if(ui->TR>MaxR) MaxR=ui->TR; }
@@ -6426,10 +6405,10 @@ void laMainLoop(){
 
         la_UpdateControllerStatus();
 
-        if(MAIN.MappingNeedRebuild){ la_RebuildInputMapping(); }
-        if(MAIN.MappingNeedEval){ la_RunInputMapping(); }
-        if(MAIN.DriverNeedRebuild){ la_RebuildDriverEval(); }
-        if(MAIN.DriverNeedEval){ la_RunDrivers(); }
+        if(MAIN.InputMapping->NeedRebuild){ la_RebuildInputMapping(); }
+        if(MAIN.InputMapping->NeedEval){ la_RunInputMapping(); }
+        if(MAIN.Drivers->NeedRebuild){ la_RebuildDriverEval(); }
+        if(MAIN.Drivers->NeedEval){ la_RunDrivers(); }
 
         for (w=MAIN.Windows.pFirst;w;w = NextW){
             NextW = w->Item.pNext;

+ 4 - 4
source/lagui/la_util.c

@@ -936,8 +936,8 @@ laListHandle* hsh16MDoHashLongPtr(laHash16M* hash, long long buckle) {
 	return &hash->Entries[(buckle>>6)&0x00FFFFFF];
 }
 laListHandle* hsh16MDoHashNUID(laHash16M* hash, char * NUID) {
-	u64bit Hash;
-	sscanf(NUID, "%ld", &Hash);
+	u64bit Hash=0;
+    for(char* c=NUID;*c;c++){ Hash=Hash*3+(*c); }
 	return hsh65536DoHashLongPtr(hash, (long)Hash);
 }
 
@@ -1190,8 +1190,8 @@ void memAssignRef(void* This, void** ptr, void* instance){
         laUseDataBlock(instance, 0, 0, ptr, la_ReferencedBlockDeleted, 0)->Additional=This;
         laUseDataBlock(This, 0, 0, instance, la_ReferrerDeleted, 0);
     }else{
-        laStopUsingDataBlock(instance, 0, This);
-        laStopUsingDataBlock(This, 0, instance);
+        laStopUsingDataBlock((*ptr), 0, This);
+        laStopUsingDataBlock(This, 0, (*ptr));
     }
     (*ptr)=instance;
 }

+ 118 - 36
source/lagui/resources/la_nodes_basic.c

@@ -466,18 +466,18 @@ void laui_MathNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn
 int OPINV_AddInputMapperPage(laOperator* a, laEvent *e){
     laRackPage* dp=memAcquireHyper(sizeof(laRackPage));
     strSafeSet(&dp->Name,"New Page");
-    lstAppendItem(&MAIN.InputMappingPages, dp); MAIN.CurrentInputMappingPage=dp;
-    laNotifyUsers("la.input_mapping_pages");
+    lstAppendItem(&MAIN.InputMapping->Pages, dp); MAIN.InputMapping->CurrentPage=dp;
+    laNotifyUsers("la.input_mapping"); laRecordAndPush(0,"la.input_mapping","Add Page", 0);
     return LA_FINISHED;
 }
 int OPINV_AddNodesRack(laOperator* a, laEvent *e){
     laRackPage* dp=a->This?a->This->EndInstance:0; if(!dp) return LA_FINISHED;
-    laNodeRack* ir=memAcquire(sizeof(laNodeRack)); 
+    laNodeRack* ir=memAcquire(sizeof(laNodeRack));
     char* type=strGetArgumentString(a->ExtraInstructionsP, "type");
     strSafeSet(&ir->Name,"New Rack");
-    lstAppendItem(&dp->Racks, ir);
-    if(strSame(type,"DRIVER")){ ir->RackType=LA_RACK_TYPE_DRIVER; laNotifyUsers("la.driver_pages"); }
-    else{ ir->RackType=LA_RACK_TYPE_INPUT; laNotifyUsers("la.input_mapping_pages"); }
+    lstAppendItem(&dp->Racks, ir); ir->ParentPage=dp;
+    if(strSame(type,"DRIVER")){ ir->RackType=LA_RACK_TYPE_DRIVER; laNotifyUsers("la.drivers"); laRecordAndPush(0,"la.drivers","Add rack", 0); }
+    else{ ir->RackType=LA_RACK_TYPE_INPUT; laNotifyUsers("la.input_mapping"); laRecordAndPush(0,"la.input_mapping","Add rack", 0); }
     return LA_FINISHED;
 
 }
@@ -489,12 +489,12 @@ int OPINV_RebuildInputMapping(laOperator* a, laEvent *e){
 laBaseNode* la_CreateInputMapperNode(laNodeRack* ir, laBaseNodeType* NodeType){
     laBaseNode* bn=memAcquire(NodeType->NodeSize);
     bn->Type=NodeType; NodeType->Init(bn); lstAppendItem(&ir->Nodes, bn); bn->InRack=ir;
-    laNotifyUsers("la.input_mapping_pages");
+    laNotifyUsers("la.input_mapping"); laRecordAndPush(0,"la.input_mapping","Add node", 0);
     return bn;
 }
 void la_DestroyInputMapperNode(laBaseNode* bn){
     lstRemoveItem(bn->InRack, bn); bn->Type->Destroy(bn);
-    laNotifyUsers("la.input_racks");
+    laNotifyUsers("la.input_racks"); laRecordAndPush(0,"la.input_mapping","Delete node", 0);
     memFree(bn);
 }
 
@@ -539,22 +539,87 @@ void laui_AddInputMapperNode(laUiList *uil, laPropPack *This, laPropPack *Extra,
     laShowItemFull(uil,c,This,"add_node_input",0,"type=VISUALIZER;text=Visualizer",0,0);
 }
 
+int OPINV_MoveNodeToRack(laOperator* a, laEvent *e){
+    laBaseNode* n=a->This?a->This->EndInstance:0; if(!n||!n->InRack) return LA_CANCELED;
+    laBaseNodeType* bnt=0; laNodeRack* target;
+
+    char* direction=strGetArgumentString(a->ExtraInstructionsP,"direction");
+    
+    if(n->InRack->RackType==LA_RACK_TYPE_DRIVER){ laNotifyUsers("la.drivers"); }
+    else{ laNotifyUsers("la.input_mapping"); }
+    if(strSame(direction,"left")) target=n->InRack->Item.pPrev; else target=n->InRack->Item.pNext;
+    if(!target) return LA_CANCELED;
+
+    lstRemoveItem(&n->InRack->Nodes, n); lstAppendItem(&target->Nodes,n); n->InRack=target;
+
+    return LA_FINISHED;
+}
+int OPINV_DeleteNode(laOperator* a, laEvent *e){
+    laBaseNode* n=a->This?a->This->EndInstance:0; if(!n||!n->InRack) return LA_CANCELED;
+    laBaseNodeType* bnt=0; laNodeRack* target;
+
+    if(n->InRack->RackType==LA_RACK_TYPE_DRIVER){ laDriverRequestRebuild(); laNotifyUsers("la.drivers"); }
+    else{ laMappingRequestRebuild(); laNotifyUsers("la.input_mapping"); }
+    lstRemoveItem(&n->InRack->Nodes, n); n->Type->Destroy(n); memLeave(n);
+
+    return LA_FINISHED;
+}
+int OPINV_MoveRack(laOperator* a, laEvent *e){
+    laNodeRack* r=a->This?a->This->EndInstance:0; if(!r) return LA_CANCELED;
+    char* direction=strGetArgumentString(a->ExtraInstructionsP,"direction");
+
+    if(strSame(direction,"left")) lstMoveUp(&r->ParentPage->Racks,r); else lstMoveDown(&r->ParentPage->Racks,r);
+
+    if(r->RackType==LA_RACK_TYPE_DRIVER){ laNotifyUsers("la.drivers");  laRecordAndPush(0,"la.drivers","Move rack", 0); }
+    else{ laNotifyUsers("la.input_mapping"); laRecordAndPush(0,"la.input_mapping","Move rack", 0); }
+    return LA_FINISHED;
+}
+int OPINV_InsertRack(laOperator* a, laEvent *e){
+    laNodeRack* rr=a->This?a->This->EndInstance:0; if(!rr) return LA_CANCELED;
+    laNodeRack* r=memAcquire(sizeof(laNodeRack));
+    strSafeSet(&r->Name,"New Rack");
+    lstInsertItemAfter(&rr->ParentPage->Racks,r,rr); r->ParentPage=rr->ParentPage; r->RackType=rr->RackType;
+    if(r->RackType==LA_RACK_TYPE_DRIVER){ laNotifyUsers("la.drivers"); laRecordAndPush(0,"la.drivers","Insert rack", 0); }
+    else{ laNotifyUsers("la.input_mapping"); laRecordAndPush(0,"la.input_mapping","Insert rack", 0); }
+    return LA_FINISHED;
+}
+int OPINV_DeleteRack(laOperator* a, laEvent *e){
+    laNodeRack* rr=a->This?a->This->EndInstance:0; if(!rr) return LA_CANCELED;
+    if(strSame(strGetArgumentString(a->ExtraInstructionsP,"confirm"),"true")){
+        laBaseNode* n; while(n=lstPopItem(&rr->Nodes)){ n->Type->Destroy(n); memLeave(n); }
+        strSafeDestroy(&rr->Name); lstRemoveItem(&rr->ParentPage->Racks, rr); memLeave(rr);
+        if(rr->RackType==LA_RACK_TYPE_DRIVER){ laNotifyUsers("la.drivers"); laRecordAndPush(0,"la.input_mapping","Delete rack", 0); }
+        else{ laNotifyUsers("la.input_mapping"); laRecordAndPush(0,"la.input_mapping","Delete rack", 0); }
+        return LA_FINISHED;
+    }
+    laEnableOperatorPanel(a,a->This,e->x,e->y,200,200,0,0,0,0,0,0,0,0,e);
+    return LA_RUNNING;
+}
+void laui_DeleteRack(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+    laShowItemFull(uil,c,This,"delete",0,"confirm=true;",0,0);
+}
+
 laPropContainer* laget_BaseNodeType(laBaseNode* bn){
     for(int i=0;i<MAIN.NodeTypeNext;i++){ if(bn->Type==MAIN.NodeTypes[i]) return MAIN.NodeTypes[i]->pc; }
     return LA_PC_IDN_GENERIC;
 }
-int laget_InputNodeGap(laNodeRack* rack_unused, laBaseNode* n){
+int laget_BaseNodeGap(laNodeRack* rack_unused, laBaseNode* n){
     return n->Gap;
 }
-void laset_InputNodeGap(laBaseNode* n, int gap){
-    if(gap<0){
-        int done=0;
-        laBaseNode* nn=n; while(nn){ if(nn->Gap>0){ nn->Gap--; done=1; break; } nn=nn->Item.pPrev; }
+void laset_BaseNodeGap(laBaseNode* n, int gap){
+    laBaseNode* nn;
+    if(gap==-1){
+        int done=0; nn=n; while(nn){ if(nn->Gap>0){ nn->Gap--; done=1; break; } nn=nn->Item.pPrev; }
         if(done){ nn=n->Item.pNext; while(nn){ if(nn->Gap>0){ nn->Gap++; break; } nn=nn->Item.pNext; } }
-    }
-    if(gap>0){
-        n->Gap+=gap;
-        laBaseNode* nn=n->Item.pNext; while(nn){ if(nn->Gap>0){ nn->Gap--; break; } nn=nn->Item.pNext; }
+    }elif(gap==1){
+        n->Gap+=gap; nn=n->Item.pNext; while(nn){ if(nn->Gap>0){ nn->Gap--; break; } nn=nn->Item.pNext; }
+    }elif(gap==-2){
+        if(n->Gap){ n->Gap--; if(nn=n->Item.pNext){ nn->Gap++; } }
+        elif(nn=n->Item.pPrev){ n->Gap=nn->Gap; nn->Gap=0; lstRemoveItem(&n->InRack->Nodes,n); lstInsertItemBefore(&n->InRack->Nodes,n,nn); }
+    }elif(gap==2){
+        if(nn=n->Item.pNext){ if(!nn->Gap){ nn->Gap=n->Gap; n->Gap=0; lstRemoveItem(&n->InRack->Nodes,n); lstInsertItemAfter(&n->InRack->Nodes,n,nn); }else{ nn->Gap--; n->Gap++; } }
+        else n->Gap++;
     }
 }
 void laset_InputNodeUserID(laInputControllerNode* n, int i){
@@ -570,6 +635,9 @@ int laget_SocketEnumArrayLength(laInputControllerNodeSocket* s){
 int laget_VisualizerArrayLength(laInputVisualizerNode* s){
     return s->In->ArrLen?s->In->ArrLen:1;
 }
+laBoxedTheme* laget_NodeGetTheme(laNodeRack* rack_unused, laBaseNode* n){
+    return 0;
+}
 
 void laRegisterNode(laBaseNodeType* type, laPropContainer* pc, laBaseNodeInitF init, laBaseNodeDestroyF destroy, laBaseNodeVisitF visit, laBaseNodeEvalF eval, int nodesize, char* udf_string){
     arrEnsureLength(&MAIN.NodeTypes, MAIN.NodeTypeNext, &MAIN.NodeTypeMax, sizeof(laBaseNode*));
@@ -597,22 +665,36 @@ void la_RegisterInputMapperOperators(){
     at=laCreateOperatorType("LA_add_input_mapper_node", "Add Node", "Add a input mapper node",0,0,0,OPINV_AddInputMapperNode,OPMOD_FinishOnData,'+',0);
     at->UiDefine=laui_AddInputMapperNode;
     laCreateOperatorType("LA_input_mapping_rebuild", "Rebuild Input Mapping", "Rebuild input mapping for evaluation",0,0,0,OPINV_RebuildInputMapping,0,L'⭮',0);
+    laCreateOperatorType("LA_move_node_to_rack", "Move Node", "Move node to another rack",0,0,0,OPINV_MoveNodeToRack,0,0,0);
+    laCreateOperatorType("LA_delete_node", "Delete Node", "Delete this node",0,0,0,OPINV_DeleteNode,0,0,0);
+    laCreateOperatorType("LA_move_rack", "Move Rack", "Move this rack",0,0,0,OPINV_MoveRack,0,0,0);
+    laCreateOperatorType("LA_insert_rack", "Insert Rack", "Insert a new rack",0,0,0,OPINV_InsertRack,0,0,0);
+    at=laCreateOperatorType("LA_delete_rack", "Delete Rack", "Delete a rack",0,0,0,OPINV_DeleteRack,OPMOD_FinishOnData,L'❌',0);
+    at->UiDefine=laui_DeleteRack;
 
     pc=laAddPropertyContainer("la_node_rack", "Input Rack", "Input rack for putting input mapping nodes",0,0,sizeof(laNodeRack),0,0,1);
     laAddStringProperty(pc,"name","Name","Name of this rack",0,0,0,0,1,offsetof(laNodeRack,Name),0,0,0,0,LA_AS_IDENTIFIER);
     p=laAddSubGroup(pc,"nodes","Nodes","Nodes under this rack","la_base_node",laget_BaseNodeType,0,0,-1,0,0,0,0,0,0,offsetof(laNodeRack,Nodes),0);
-    laSubGroupExtraFunctions(p,0,0,laget_InputNodeGap);
+    laSubGroupExtraFunctions(p,0,laget_NodeGetTheme,laget_BaseNodeGap);
+    laAddSubGroup(pc,"parent_page","Parent Page","Parent page of this rack","la_rack_page",0,0,0,offsetof(laNodeRack,ParentPage),0,0,0,0,0,0,0,LA_UDF_REFER);
+    laAddIntProperty(pc,"type", "Type", "Type of the rack", 0,0,0,0,0,0,0,0,offsetof(laNodeRack,RackType),0,0,0,0,0,0,0,0,0,0,LA_READ_ONLY);
     laAddOperatorProperty(pc,"add_node_input","Add Node","Add a node into this rack","LA_add_input_mapper_node",'+',0);
     laAddOperatorProperty(pc,"add_node_driver","Add Node","Add a node into this rack","LA_add_driver_node",'+',0);
+    laAddOperatorProperty(pc,"insert_rack","Insert Rack","Insert a rack","LA_insert_rack",'+',0);
+    laAddOperatorProperty(pc,"move","Move Rack","Move this rack","LA_move_rack",0,0);
+    laAddOperatorProperty(pc,"delete","Delete Rack","Delete this rack","LA_delete_rack",0,0);
 
     pc=laAddPropertyContainer("la_base_node", "Input Node", "Input logic node",0,0,sizeof(laBaseNode),0,0,1);
     LA_PC_IDN_GENERIC=pc;
     laAddStringProperty(pc,"name","Name","Name of this input node",0,0,0,0,1,offsetof(laBaseNode,Name),0,0,0,0,LA_AS_IDENTIFIER);
-    laAddIntProperty(pc,"__gap", "Gap", "Gap of the node", 0,0,0,0,0,0,0,0,offsetof(laBaseNode,Gap),0,laset_InputNodeGap,0,0,0,0,0,0,0,0,0);
+    laAddIntProperty(pc,"__gap", "Gap", "Gap of the node", 0,0,0,0,0,0,0,0,offsetof(laBaseNode,Gap),0,laset_BaseNodeGap,0,0,0,0,0,0,0,0,0);
     laAddSubGroup(pc,"internal_type","Internal Type","Internal node type","any_pointer",0,0,0,offsetof(laBaseNode,Type),0,0,0,0,0,0,0,LA_READ_ONLY|LA_UDF_REFER);
+    laAddOperatorProperty(pc,"move","Move","Move node across racks","LA_move_node_to_rack",0,0);
+    laAddOperatorProperty(pc,"delete","Delete","Delete node","LA_delete_node",0,0);
+    laAddSubGroup(pc,"in_rack","In Rack","The rack this node is in","la_node_rack",0,0,0,offsetof(laBaseNode,InRack),0,0,0,0,0,0,0,LA_UDF_REFER);
 
     pc=laAddPropertyContainer("la_input_controller_node", "Controller output", "Output controller values",0,laui_ControllerNode,sizeof(laInputControllerNode),0,0,1);
-    LA_PC_IDN_CONTROLLER=pc;
+    LA_PC_IDN_CONTROLLER=pc; laPropContainerExtraFunctions(pc,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
     laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
     laAddIntProperty(pc,"user_id", "User ID", "Which controller should the data come from", 0,0,0,0,0,0,0,0,offsetof(laInputControllerNode,UserID),0,0,0,0,0,0,0,0,0,0,0);
     laAddSubGroup(pc,"out0","Out 0","Output 0","la_input_controller_node_socket",0,0,0,offsetof(laInputControllerNode, Sockets[0]),0,0,0,0,0,0,0,LA_UDF_LOCAL);
@@ -634,7 +716,7 @@ void la_RegisterInputMapperOperators(){
     laAddSubGroup(pc, "out", "Out","Output value","la_out_socket",0,0,0,offsetof(laInputControllerNodeSocket,Out),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 
     pc=laAddPropertyContainer("la_input_visualizer_node", "Visualizer", "Visualizer node",0,laui_InputVisualizeNode,sizeof(laInputVisualizerNode),0,0,1);
-    LA_PC_IDN_VISUALIZER=pc;
+    LA_PC_IDN_VISUALIZER=pc; laPropContainerExtraFunctions(pc,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
     laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
     laAddSubGroup(pc, "in", "In","Input value","la_in_socket",0,0,0,offsetof(laInputVisualizerNode,In),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddFloatProperty(pc,"axis", "🡘", "Axis value", LA_WIDGET_VALUE_METER,0,0,1,-1,0,0,0,offsetof(laInputVisualizerNode,RealVal),0,0,0,0,0,0,0,0,0,0,LA_READ_ONLY);
@@ -644,7 +726,7 @@ void la_RegisterInputMapperOperators(){
     laAddEnumItemAs(p,"ACTIVE", "Active", "Button is pressed", 1, 0);
 
     pc=laAddPropertyContainer("la_split_node", "Split", "Split node",0,laui_SplitNode,sizeof(laSplitNode),0,0,1);
-    LA_PC_IDN_SPLIT=pc;
+    LA_PC_IDN_SPLIT=pc; laPropContainerExtraFunctions(pc,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
     laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
     laAddSubGroup(pc,"in", "In","Input value","la_in_socket",0,0,0,offsetof(laSplitNode,In),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"out0","Out 0","Output 0","la_split_node_out_socket",0,0,0,offsetof(laSplitNode, Out[0]),0,0,0,0,0,0,0,LA_UDF_LOCAL);
@@ -661,7 +743,7 @@ void la_RegisterInputMapperOperators(){
     laAddSubGroup(pc, "out", "Out","Output value","la_out_socket",0,0,0,offsetof(laSplitNodeOutSocket,Out),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 
     pc=laAddPropertyContainer("la_switch_node", "Switch", "Switch node",0,laui_SwitchNode,sizeof(laSwitchNode),0,0,1);
-    LA_PC_IDN_SWITCH=pc;
+    LA_PC_IDN_SWITCH=pc; laPropContainerExtraFunctions(pc,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
     laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
     laAddSubGroup(pc,"out", "Out","Output value","la_out_socket",0,0,0,offsetof(laSwitchNode, Out),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"in0","In 0","Input 0","la_switch_node_in_socket",0,0,0,offsetof(laSwitchNode, In[0]),0,0,0,0,0,0,0,LA_UDF_LOCAL);
@@ -679,7 +761,7 @@ void la_RegisterInputMapperOperators(){
     laAddSubGroup(pc, "in", "In","Input value","la_in_socket",0,0,0,offsetof(laSwitchNodeInSocket,In),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 
     pc=laAddPropertyContainer("la_combine_node", "Combine", "Combine node",0,laui_CombineNode,sizeof(laCombineNode),0,0,1);
-    LA_PC_IDN_COMBINE=pc;
+    LA_PC_IDN_COMBINE=pc; laPropContainerExtraFunctions(pc,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
     laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
     laAddSubGroup(pc,"out", "Out","Output value","la_out_socket",0,0,0,offsetof(laCombineNode, Out),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"out_int", "Out Int","Output value in int format","la_out_socket",0,0,0,offsetof(laCombineNode, OutInt),0,0,0,0,0,0,0,LA_UDF_SINGLE);
@@ -694,7 +776,7 @@ void la_RegisterInputMapperOperators(){
     laAddSubGroup(pc,"in7","In 7","Input 7","la_switch_node_in_socket",0,0,0,offsetof(laCombineNode, In[7]),0,0,0,0,0,0,0,LA_UDF_LOCAL);
 
     pc=laAddPropertyContainer("la_values_node", "Values", "Values node",0,laui_ValuesNode,sizeof(laValuesNode),0,0,1);
-    LA_PC_IDN_VALUES=pc;
+    LA_PC_IDN_VALUES=pc; laPropContainerExtraFunctions(pc,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
     laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
     laAddSubGroup(pc,"out0","Out 0","Output 0","la_split_node_out_socket",0,0,0,offsetof(laValuesNode, Out[0]),0,0,0,0,0,0,0,LA_UDF_LOCAL);
     laAddSubGroup(pc,"out1","Out 1","Output 1","la_split_node_out_socket",0,0,0,offsetof(laValuesNode, Out[1]),0,0,0,0,0,0,0,LA_UDF_LOCAL);
@@ -738,7 +820,7 @@ void la_RegisterInputMapperOperators(){
     p=laAddEnumProperty(pc,"valuee7","SW","Enum Value 7",LA_WIDGET_ENUM_CYCLE,0,0,0,0,offsetof(laValuesNode, ValuesE[7]),0,0,0,0,0,0,0,0,0,0);la_AddValuesNodeEnumValue(p);
 
     pc=laAddPropertyContainer("la_matrix_node", "Matrix", "Matrix node",0,laui_MatrixNode,sizeof(laMatrixNode),0,0,1);
-    LA_PC_IDN_MATRIX=pc;
+    LA_PC_IDN_MATRIX=pc; laPropContainerExtraFunctions(pc,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
     laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
     laAddSubGroup(pc,"in_l", "L","Left input","la_in_socket",0,0,0,offsetof(laMatrixNode, InL),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"in_r", "R","Right input","la_in_socket",0,0,0,offsetof(laMatrixNode, InR),0,0,0,0,0,0,0,LA_UDF_SINGLE);
@@ -748,7 +830,7 @@ void la_RegisterInputMapperOperators(){
     laAddEnumItemAs(p,"INV", "Invert", "Invert L or R", LA_MATRIX_NODE_OP_INV, 0);
 
     pc=laAddPropertyContainer("la_math_node", "Math", "Math node",0,laui_MathNode,sizeof(laMathNode),0,0,1);
-    LA_PC_IDN_MATH=pc;
+    LA_PC_IDN_MATH=pc; laPropContainerExtraFunctions(pc,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
     laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
     laAddFloatProperty(pc, "vl", "L", "Left value", 0,0,0,0,0,0,0,0,offsetof(laMathNode, ValueL),0,0,0,0,0,0,0,0,0,0,0);
     laAddFloatProperty(pc, "vr", "R", "Right value", 0,0,0,0,0,0,0,0,offsetof(laMathNode, ValueR),0,0,0,0,0,0,0,0,0,0,0);
@@ -789,28 +871,28 @@ void laSetExtraNodeFunctions(laUiDefineFunc AddInputNodes, laUiDefineFunc AddDri
     MAIN.ExtraGetInputNodeType=GetInputNodeType; MAIN.ExtraGetDriverNodeType=GetDriverNodeType;
 }
 
-void laMappingRequestRebuild(){ MAIN.MappingNeedRebuild=1; }
-void laMappingRequestEval(){ MAIN.MappingNeedEval=1; }
+void laMappingRequestRebuild(){ MAIN.InputMapping->NeedRebuild=1; }
+void laMappingRequestEval(){ MAIN.InputMapping->NeedEval=1; }
 
 int la_RunInputMapping(){
-    MAIN.MappingNeedEval = 0;
-    for(laListItemPointer*lip=MAIN.InputMappingEval.pFirst;lip;lip=lip->pNext){
+    MAIN.InputMapping->NeedEval = 0;
+    for(laListItemPointer*lip=MAIN.InputMapping->Eval.pFirst;lip;lip=lip->pNext){
         laBaseNode* n=lip->p; n->Type->Eval(n);
     }
     return 1;
 }
 int la_RebuildInputMapping(){
-    MAIN.MappingNeedRebuild = 0;
-    while(lstPopPointer(&MAIN.InputMappingEval));
-    laListHandle pending={0}; laRackPage* rp=MAIN.CurrentInputMappingPage; if(!rp)return LA_DAG_FLAG_PERM;
+    MAIN.InputMapping->NeedRebuild = 0;
+    while(lstPopPointer(&MAIN.InputMapping->Eval));
+    laListHandle pending={0}; laRackPage* rp=MAIN.InputMapping->CurrentPage; if(!rp)return LA_DAG_FLAG_PERM;
     for(laNodeRack* ir=rp->Racks.pFirst;ir;ir=ir->Item.pNext){
         for(laBaseNode*bn=ir->Nodes.pFirst;bn;bn=bn->Item.pNext){ lstAppendPointer(&pending,bn); bn->Eval=0; }
     }
     laBaseNode*n;int result=LA_DAG_FLAG_PERM; laListItemPointer*NextLip;
     for(laListItemPointer*lip=pending.pFirst;lip;lip=NextLip){ n=lip->p; NextLip=lip->pNext;
         if(n->Eval&LA_DAG_FLAG_PERM) continue;
-        result=n->Type->Visit(n,&MAIN.InputMappingEval); if(result==LA_DAG_FLAG_ERR){ while(lstPopPointer(&pending)); break; }
+        result=n->Type->Visit(n,&MAIN.InputMapping->Eval); if(result==LA_DAG_FLAG_ERR){ while(lstPopPointer(&pending)); break; }
     }
-    if(result==LA_DAG_FLAG_ERR){ while(lstPopPointer(&MAIN.InputMappingEval)); return LA_DAG_FLAG_ERR; }
+    if(result==LA_DAG_FLAG_ERR){ while(lstPopPointer(&MAIN.InputMapping->Eval)); return LA_DAG_FLAG_ERR; }
     return LA_DAG_FLAG_PERM;
 }

+ 20 - 14
source/lagui/resources/la_properties.c

@@ -410,9 +410,6 @@ void *laget_CanvasExtra(laUiItem *ui){
 void laget_MainIdentifier(laUDF *udf, char *result, char** here){
     strcpy(result, "MAIN");
 }
-void laget_TNSIdentifier(laUDF *udf, char *result, char** here){
-    strcpy(result, "TNS");
-}
 void laget_UDFFilePath(laUDF *udf, char *result, char** here){
     *here=udf->FileName->Ptr;
 }
@@ -481,7 +478,10 @@ void laread_ConditionNodeType(laUiConditionNode *ucn, int Type){
 }
 
 void* laget_FirstDriverPage(void* unused, void* unused2){
-    return MAIN.DriverPages.pFirst;
+    return MAIN.Drivers->Pages.pFirst;
+}
+void* laget_CurrentRackPage(laRackPageCollection* c){
+    return c->CurrentPage;
 }
 
 void *tnsget_TnsMain(void *unused){
@@ -895,10 +895,8 @@ void la_RegisterInternalProps(){
             laSubGroupDetachable(sp, laget_DetachedControllerFirst, laget_ListNext);
             LA_PROP_CONTROLLER=sp;
             
-            laAddSubGroup(p, "input_mapping_pages", "Input Mapping Pages", "Input mapping pages","la_rack_page",0,0,0,offsetof(LA,CurrentInputMappingPage),0,0,0,0,0,0,offsetof(LA,InputMappingPages),0);
-            
-            sp=laAddSubGroup(p, "driver_pages", "Driver Pages", "Pages of driver nodes","la_rack_page",0,0,0,offsetof(LA,CurrentDriverPage),0,0,0,0,0,0,offsetof(LA,DriverPages),0);
-            laSubGroupDetachable(sp, laget_FirstDriverPage, laget_ListNext);
+            laAddSubGroup(p, "input_mapping", "Input Mapping", "Input mapping page collection","la_input_mapping_collection",0,0,0,offsetof(LA,InputMapping),0,0,0,0,0,0,0,LA_UDF_SINGLE);
+            laAddSubGroup(p, "drivers", "Drivers", "Driver page collection","la_driver_collection",0,0,0,offsetof(LA,Drivers),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 
             laAddStringProperty(p, "identifier", "Identifier", "Identifier", 0, 0, 0, 0, 0, 0, 0, laget_MainIdentifier, 0, 0, LA_AS_IDENTIFIER|LA_READ_ONLY);
             laAddSubGroup(p, "test_ucn", "TEST UCN", "---", "udf_content_node",0, 0, 0, offsetof(LA, TEST_Link), 0, 0, 0, 0, 0, 0, 0, 0);
@@ -907,6 +905,16 @@ void la_RegisterInternalProps(){
             laAddStringProperty(p, "example_string", "Example String", "Example string", 0, 0, 0, 0, 1, offsetof(LA,example_string), 0, 0, 0, 0, 0);
         }
 
+        p = laAddPropertyContainer("la_input_mapping_collection", "Input Mapping Collection", "Input mapping collection", 0, 0, sizeof(laRackPageCollection), 0, 0, 1);{
+            laAddSubGroup(p, "pages", "Pages", "Rack pages","la_rack_page",0,0,0,offsetof(laRackPageCollection,CurrentPage),0,0,0,0,0,0,offsetof(laRackPageCollection,Pages),0);
+            laAddSubGroup(p, "current_page", "Current Page", "Current page","la_rack_page",0,0,0,offsetof(laRackPageCollection,CurrentPage),0,0,0,0,0,0,0,LA_UDF_REFER);
+        }
+        p = laAddPropertyContainer("la_driver_collection", "Driver Collection", "Driver collection", 0, 0, sizeof(laRackPageCollection), 0, 0, 1);{
+            laAddSubGroup(p, "pages", "Pages", "Rack pages","la_rack_page",0,0,0,-1,0,laget_CurrentRackPage,0,0,0,0,offsetof(laRackPageCollection,Pages),0);
+            sp=laAddSubGroup(p, "current_page", "Current Page", "Current page","la_rack_page",0,0,0,offsetof(laRackPageCollection,CurrentPage),0,0,0,0,0,0,0,LA_UDF_REFER);
+            laSubGroupDetachable(sp,laget_FirstDriverPage,laget_ListNext);
+        }
+
         p = laAddPropertyContainer("la_rack_page", "Rack Page", "A page of nodes", 0, laui_IdentifierOnly, sizeof(laRackPage), 0, 0, 2);{
             laAddStringProperty(p, "name", "Name", "Name of the page", 0, 0, 0, 0, 1, offsetof(laRackPage, Name), 0, 0, 0, 0, LA_AS_IDENTIFIER);
             laAddSubGroup(p, "racks", "Racks", "Racks for nodes","la_node_rack",0,0,0,-1,0,0,0,0,0,0,offsetof(laRackPage,Racks),0);
@@ -1243,8 +1251,6 @@ void la_RegisterInternalProps(){
         laSubGroupDetachable(sp, tnsget_detached_FirstTexture, laget_ListNext);
 
         //laAddSubGroup(p, "Render Buffers", "Storing All Render Buffers In Current Program Instance", "tns_render_buffer",0, 0, 0, offsetof(tnsMain, ActiveRenderBuffer), 0, 0, 0, 0, 0, 0, 0, 0, offsetof(tnsMain, RenderBuffers), 0);
-
-        laAddStringProperty(p, "identifier", "Identifier", "Identifier", 0, 0, 0, 0, 0, 0, 0, laget_TNSIdentifier, 0, 0, LA_AS_IDENTIFIER|LA_READ_ONLY);
     }
 
     p = laAddPropertyContainer("tns_texture", "TNS Texture Item", "A Texture Descriptor With GL Handle", 0, 0, sizeof(tnsTexture), 0, 0, 0);{
@@ -1279,7 +1285,7 @@ void la_RegisterInternalProps(){
     }
 
     p = laAddPropertyContainer("tns_object", "Object", "3D Object Item", 0, 0, sizeof(tnsObject), tnspost_Object, 0, 2);{
-        laPropContainerExtraFunctions(p,0,0,tnspropagate_Object);
+        laPropContainerExtraFunctions(p,0,0,tnspropagate_Object,0);
         TNS_PC_OBJECT_GENERIC=p;
         laAddStringProperty(p, "name", "Object Name", "The Name Of The Object", 0, 0, 0, 0, 1, offsetof(tnsObject, Name), 0, 0, 0, 0, LA_AS_IDENTIFIER);
         ep = laAddEnumProperty(p, "show", "Show", "Show object in the viewport", 0, 0, 0, 0, 0, offsetof(tnsObject, Show), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);{
@@ -1315,7 +1321,7 @@ void la_RegisterInternalProps(){
         laAddSubGroup(p, "children", "Children", "The Children Of This Object", "tns_child_object",0, 0, 0, -1, 0, 0, 0, 0, 0, 0, offsetof(tnsObject, ChildObjects), 0);
     }
     p = laAddPropertyContainer("tns_mesh_object", "Mesh Object", "Mesh object", 0, 0, sizeof(tnsMeshObject), tnspost_Object, 0, 2);{
-        laPropContainerExtraFunctions(p,0,tnstouched_Object,tnspropagate_Object);
+        laPropContainerExtraFunctions(p,0,tnstouched_Object,tnspropagate_Object,0);
         TNS_PC_OBJECT_MESH=p;
         laAddStringProperty(p, "name", "Object Name", "The Name Of The Object", 0, 0, 0, 0, 1, offsetof(tnsObject, Name), 0, 0, 0, 0, LA_AS_IDENTIFIER);
         laAddSubGroup(p, "base", "Base", "Object base", "tns_object",0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, LA_UDF_LOCAL);
@@ -1340,7 +1346,7 @@ void la_RegisterInternalProps(){
         //laAddIntProperty(p, "maxf", "Max Face", "Max Face count", 0,0,0,0,0,0,0,0,offsetof(tnsMeshObject, maxf),0,0,0,0,0,0,0,0,0,0,LA_READ_ONLY);
     }
     p = laAddPropertyContainer("tns_camera", "Camera", "Camera object", L'📷', 0, sizeof(tnsCamera), 0, 0, 2);{
-        laPropContainerExtraFunctions(p,0,0,tnspropagate_Object);
+        laPropContainerExtraFunctions(p,0,0,tnspropagate_Object,0);
         TNS_PC_OBJECT_CAMERA=p;
         laAddStringProperty(p, "name", "Object Name", "The Name Of The Object", 0, 0, 0, 0, 1, offsetof(tnsObject, Name), 0, 0, 0, 0, LA_AS_IDENTIFIER);
         laAddSubGroup(p, "base", "Base", "Object base", "tns_object",0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, LA_UDF_LOCAL);
@@ -1355,7 +1361,7 @@ void la_RegisterInternalProps(){
         laAddOperatorProperty(p, "set_active", "Set Active", "Set this camera as the active one", "TNS_set_active_camera", 0, 0);
     }
     p = laAddPropertyContainer("tns_light", "Light", "Light object", L'🔅', 0, sizeof(tnsLight), 0, 0, 2);{
-        laPropContainerExtraFunctions(p,0,0,tnspropagate_Object);
+        laPropContainerExtraFunctions(p,0,0,tnspropagate_Object,0);
         TNS_PC_OBJECT_LIGHT=p;
         laAddStringProperty(p, "name", "Object Name", "The Name Of The Object", 0, 0, 0, 0, 1, offsetof(tnsObject, Name), 0, 0, 0, 0, LA_AS_IDENTIFIER);
         laAddSubGroup(p, "base", "Base", "Object base", "tns_object",0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, LA_UDF_LOCAL);

+ 49 - 40
source/lagui/resources/la_templates.c

@@ -77,24 +77,26 @@ void laui_DefaultPropDetails(laUiList *uil, laPropPack *This, laPropPack *Extra,
     strSafeDestroy(&s);
 }
 
+void laui_DefaultNodeOperationsPropUiDefine(laUiList *uil, laPropPack *This, laPropPack *OperatorProps, laColumn *UNUSED, int context){
+    laColumn *c = laFirstColumn(uil); laColumn* cl,*cr; laSplitColumn(uil,c,0.5); cl=laLeftColumn(c,0);cr=laRightColumn(c,0);
+    laShowItemFull(uil, c, This, "base.name",LA_WIDGET_STRING_PLAIN,0,0,0);
+    laShowItemFull(uil, cl, This, "base.move",0,"direction=left;text=Move Left;icon=🡰;",0,0);
+    laShowItemFull(uil, cr, This, "base.move",0,"text=Move Right;icon=🡲;",0,0);
+    laShowItemFull(uil, c, This, "base.delete",0,0,0,0);
+}
 void laui_DefaultPropUiDefine(laUiList *uil, laPropPack *This, laPropPack *OperatorProps, laColumn *UNUSED, int context){
     laColumn *c;
     laProp *p, *gp;
 
     if (OperatorProps && OperatorProps->LastPs){
-        c = laFirstColumn(uil);
-
-        gp = OperatorProps->LastPs->p;
+        c = laFirstColumn(uil); gp = OperatorProps->LastPs->p;
         for (p = gp->SubProp->Props.pFirst; p; p = p->Item.pNext){
             laShowItem(uil, c, OperatorProps, p->Identifier);
         }
     }
-
     if (!This || !This->LastPs) return;
 
-    c = laFirstColumn(uil);
-
-    gp = This->LastPs->p;
+    c = laFirstColumn(uil); gp = This->LastPs->p;
     for (p = gp->SubProp->Props.pFirst; p; p = p->Item.pNext){
         //la_ShowGeneralPropItem(uil, c, This, gp, p, 0, 0, 0);
         laShowItem(uil, c, This, p->Identifier);
@@ -865,7 +867,7 @@ void laui_FileBrowserFileItem(laUiList *uil, laPropPack *This, laPropPack *OP_UN
     laUiItem *ui;
 
     laSplitColumn(0, c, 0.1);
-    cll = laLeftColumn(c, 30);
+    cll = laLeftColumn(c, 1);
     clr = laRightColumn(c, 0);
     laSplitColumn(0, clr, 0.7);
     clrl = laLeftColumn(clr, 0);
@@ -909,7 +911,7 @@ void laui_FileBrowserFileList(laUiList *uil, laPropPack *THIS_UNUSED, laPropPack
     laUiItem *b;
 
     laSplitColumn(uil, col, 0.25);
-    cl = laLeftColumn(col, 250);
+    cl = laLeftColumn(col, 10);
     cr = laRightColumn(col, 0);
 
     laShowItem(uil, cr, Operator, "path");
@@ -1118,6 +1120,35 @@ void laui_LogItem(laUiList *uil, laPropPack *Base, laPropPack *OperatorInst, laC
     laShowItem(uil, ExtraColumns, Base, "content")->Flags|=LA_TEXT_LINE_WRAP|LA_TEXT_USE_NEWLINE|LA_TEXT_MONO;
 }
 
+void laui_NodeRack(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
+    laNodeRack* r=This->EndInstance;
+    laColumn* c=laFirstColumn(uil),*cl, *cr;
+    laShowLabel(uil,c,"\n",0,0);
+    laUiItem* b=laBeginRow(uil,c,0,0);
+    laShowItemFull(uil,c,This,"move",0,"icon=🡰;direction=left;",0,0)->Flags|=LA_UI_FLAGS_ICON;
+    laUiItem* u=laShowItem(uil,c,This,"name"); u->Flags|=LA_UI_FLAGS_NO_DECAL|LA_TEXT_ALIGN_CENTER; u->Expand=1;
+    laShowItemFull(uil,c,This,"move",0,"icon=🡲;direction=right;",0,0)->Flags|=LA_UI_FLAGS_ICON;
+    laEndRow(uil,b);
+    laShowLabel(uil,c,"\n",0,0);
+    laShowItem(uil,c,This,"nodes")->Flags|=LA_UI_FLAGS_NO_DECAL;
+    laShowLabel(uil,c,"\n",0,0);
+    b=laBeginRow(uil,c,0,0);
+    if(r->RackType==LA_RACK_TYPE_INPUT){ laShowItem(uil,c,This,"add_node_input")->Expand=1; }
+    else{ laShowItem(uil,c,This,"add_node_driver")->Expand=1; }
+    laShowItem(uil,c,This,"delete")->Flags|=LA_UI_FLAGS_ICON;
+    laShowItem(uil,c,This,"insert_rack");
+    laEndRow(uil,b);
+    laShowLabel(uil,c,"\n\n\n",0,0);
+}
+void laui_RackPage(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil);
+    laUiItem* g=laMakeGroup(uil,c,"Racks",0);{ g->Flags|=/*LA_UI_FLAGS_NO_GAP|LA_UI_FLAGS_NO_DECAL|*/LA_UI_FLAGS_NODE_CONTAINER;
+        laUiList* gu=g->Page; laColumn* gc=laFirstColumn(gu); gu->AllowScale=1; gu->HeightCoeff=-3; g->State=LA_UI_ACTIVE;
+        laUiItem* hui=laShowItemFull(gu,gc,This,"racks",0,0,laui_NodeRack,0); hui->Expand=15; hui->Flags|=LA_UI_FLAGS_NO_DECAL;
+    }
+    laShowItemFull(uil,c,This,"add_rack",0,context?"type=DRIVER;":0,0,0);
+}
+
 
 void laui_UserPreference(laUiList *uil, laPropPack *Base, laPropPack *OperatorInst, laColumn *ExtraColumns, int context){
     laColumn* c = laFirstColumn(uil),*cl,*cr; laSplitColumn(uil,c,0.5);cl=laLeftColumn(c,0);cr=laRightColumn(c,0);
@@ -1416,56 +1447,34 @@ void laui_GameController(laUiList *uil, laPropPack *This, laPropPack *Extra, laC
 void lauidetached_GameController(laPanel* p){
     la_MakeDetachedProp(p, "la.controllers", "controllers");
 }
-void laui_NodeRack(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
-    laNodeRack* r=This->EndInstance;
-    laColumn* c=laFirstColumn(uil),*cl, *cr;
-    laShowItem(uil,c,This,"name")->Flags|=LA_UI_FLAGS_NO_DECAL|LA_TEXT_ALIGN_CENTER;
-    laShowItem(uil,c,This,"nodes")->Flags|=LA_UI_FLAGS_NO_DECAL;
-    laShowLabel(uil,c,"\n\n\n",0,0);
-    if(r->RackType==LA_RACK_TYPE_INPUT){
-        laShowItem(uil,c,This,"add_node_input");
-    }else{
-        laShowItem(uil,c,This,"add_node_driver");
-    }
-    laShowLabel(uil,c,"\n\n\n",0,0);
-}
-void laui_RackPage(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
-    laColumn* c=laFirstColumn(uil);
-    laUiItem* g=laMakeGroup(uil,c,"Racks",0);{ g->Flags|=/*LA_UI_FLAGS_NO_GAP|LA_UI_FLAGS_NO_DECAL|*/LA_UI_FLAGS_NODE_CONTAINER;
-        laUiList* gu=g->Page; laColumn* gc=laFirstColumn(gu); gu->AllowScale=1; gu->HeightCoeff=-3; g->State=LA_UI_ACTIVE;
-        laUiItem* hui=laShowItemFull(gu,gc,This,"racks",0,0,laui_NodeRack,0); hui->Expand=15; hui->Flags|=LA_UI_FLAGS_NO_DECAL;
-    }
-    laShowItemFull(uil,c,This,"add_rack",0,context?"type=DRIVER;":0,0,0);
-}
 void laui_InputMapper(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil),*cl, *cr;
     laSplitColumn(uil,c,0.6); cl=laLeftColumn(c,0); cr=laRightColumn(c,0);
     
     laUiItem* b=laBeginRow(uil,cl,0,0);
-    laUiItem* b2=laOnConditionThat(uil,cl,laPropExpression(0,"la.input_mapping_pages"));{
-        laUiItem* b3=laOnConditionThat(uil,cl,laPropExpression(0,"la.input_mapping_pages"));{
-            laShowItem(uil,cr,0,"la.input_mapping_pages.name")->Expand=1;
+    laUiItem* b2=laOnConditionThat(uil,cl,laPropExpression(0,"la.input_mapping.pages"));{
+        laUiItem* b3=laOnConditionThat(uil,cl,laPropExpression(0,"la.input_mapping.current_page"));{
+            laShowItem(uil,cr,0,"la.input_mapping.current_page.name")->Expand=1;
             laShowItem(uil,c,Extra,"LA_input_mapping_rebuild")->Flags|=LA_UI_FLAGS_ICON;
         }laEndCondition(uil,b3);
     }laEndCondition(uil,b2);
     laShowItem(uil,cl,0,"LA_add_input_mapping_page")->Flags|=LA_UI_FLAGS_ICON;
     laEndRow(uil,b);
-    b2=laOnConditionThat(uil,cr,laPropExpression(0,"la.input_mapping_pages"));{
-        laShowItemFull(uil,cr,0,"la.input_mapping_pages",LA_WIDGET_COLLECTION_SELECTOR,0,0,0);
+    b2=laOnConditionThat(uil,cr,laPropExpression(0,"la.input_mapping.pages"));{
+        laShowItemFull(uil,cr,0,"la.input_mapping.pages",LA_WIDGET_COLLECTION_SELECTOR,0,0,0);
     }laEndCondition(uil,b2);
     
-    laShowItemFull(uil,c,0,"la.input_mapping_pages",LA_WIDGET_COLLECTION_SINGLE,0,laui_RackPage,0);
+    laShowItemFull(uil,c,0,"la.input_mapping.current_page",LA_WIDGET_COLLECTION_SINGLE,0,laui_RackPage,0);
 }
-
 void lauidetached_Drivers(laPanel* p){
-    la_MakeDetachedProp(p, "la.driver_pages", "page");
+    la_MakeDetachedProp(p, "la.drivers.pages", "page");
 }
 void laui_Drivers(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil),*cl, *cr;
     laSplitColumn(uil,c,0.6); cl=laLeftColumn(c,0); cr=laRightColumn(c,0);
     
     laUiItem* b=laBeginRow(uil,cl,0,0);
-    laUiItem* b2=laOnConditionThat(uil,cl,laPropExpression(0,"la.driver_pages"));{
+    laUiItem* b2=laOnConditionThat(uil,cl,laPropExpression(0,"la.drivers.pages"));{
         laUiItem* b3=laOnConditionThat(uil,cl,laPropExpression(Extra,"page"));{
             laShowItem(uil,cr,Extra,"page.name")->Expand=1;
             laShowItem(uil,c,Extra,"LA_driver_rebuild")->Flags|=LA_UI_FLAGS_ICON;
@@ -1473,7 +1482,7 @@ void laui_Drivers(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *
     }laEndCondition(uil,b2);
     laShowItem(uil,cl,0,"LA_add_driver_page")->Flags|=LA_UI_FLAGS_ICON;
     laEndRow(uil,b);
-    b2=laOnConditionThat(uil,cr,laPropExpression(0,"la.driver_pages"));{
+    b2=laOnConditionThat(uil,cr,laPropExpression(0,"la.drivers.pages"));{
         laShowItemFull(uil,cr,Extra,"page",LA_WIDGET_COLLECTION_SELECTOR,0,0,0);
     }laEndCondition(uil,b2);
     

+ 15 - 15
source/lagui/resources/la_tns_drivers.c

@@ -116,20 +116,20 @@ void tnsui_MakeTransformNode(laUiList *uil, laPropPack *This, laPropPack *Extra,
 int OPINV_AddDriverPage(laOperator* a, laEvent *e){
     laRackPage* dp=memAcquire(sizeof(laRackPage));
     strSafeSet(&dp->Name,"New Page");
-    lstAppendItem(&MAIN.DriverPages, dp); MAIN.CurrentDriverPage=dp;
-    laNotifyUsers("la.driver_pages");
+    lstAppendItem(&MAIN.Drivers->Pages, dp); MAIN.Drivers->CurrentPage=dp;
+    laNotifyUsers("la.drivers.pages"); laRecordAndPush(0,"la.drivers","Add page", 0);
     return LA_FINISHED;
 }
 
 laBaseNode* la_CreateDriverNode(laNodeRack* ir, laBaseNodeType* NodeType){
     laBaseNode* bn=memAcquire(NodeType->NodeSize);
     bn->Type=NodeType; NodeType->Init(bn); lstAppendItem(&ir->Nodes, bn); bn->InRack=ir;
-    laNotifyUsers("la.driver_pages");
+    laNotifyUsers("la.drivers.pages"); laRecordAndPush(0,"la.drivers","Add node", 0);
     return bn;
 }
 void la_DestroyDriverNode(laBaseNode* bn){
     lstRemoveItem(bn->InRack, bn); bn->Type->Destroy(bn);
-    laNotifyUsers("la.driver_pages");
+    laNotifyUsers("la.drivers.pages"); laRecordAndPush(0,"la.drivers","Delete node", 0);
     memFree(bn);
 }
 int OPINV_AddDriverNode(laOperator* a, laEvent *e){
@@ -192,13 +192,13 @@ void tns_RegisterNodes(){
     laCreateOperatorType("LA_driver_rebuild", "Rebuild Drivers", "Rebuild drivers for evaluation",0,0,0,OPINV_RebuildDrivers,0,L'⭮',0);
 
     pc=laAddPropertyContainer("tns_transform_node", "Transform", "Transform objects",0,tnsui_TransformNode,sizeof(tnsTransformNode),0,0,1);
-    TNS_PC_IDN_TRANSFORM=pc;
+    TNS_PC_IDN_TRANSFORM=pc; laPropContainerExtraFunctions(pc,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
     laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
     laAddSubGroup(pc,"mat", "Mat","Input matrix","la_in_socket",0,0,0,offsetof(tnsTransformNode,Mat),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"target", "Target","Target object","tns_object",0,LA_WIDGET_COLLECTION_SELECTOR,laui_IdentifierOnly,offsetof(tnsTransformNode,Target),tnsget_FirstObject,0,laget_ListNext,0,0,0,0,LA_UDF_REFER);
     
     pc=laAddPropertyContainer("tns_make_transform_node", "Make Transform", "Make Transform matrix",0,tnsui_MakeTransformNode,sizeof(tnsMakeTransformNode),0,0,1);
-    TNS_PC_IDN_MAKE_TRANSFORM=pc;
+    TNS_PC_IDN_MAKE_TRANSFORM=pc; laPropContainerExtraFunctions(pc,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
     laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
     laAddSubGroup(pc,"out", "Out","Output matrix","la_out_socket",0,0,0,offsetof(tnsMakeTransformNode,Out),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"loc", "Location","Location","la_in_socket",0,0,0,offsetof(tnsMakeTransformNode,Loc),0,0,0,0,0,0,0,LA_UDF_SINGLE);
@@ -214,21 +214,21 @@ void tns_RegisterNodes(){
     LA_IDN_REGISTER(TNS_IDN_MAKE_TRANSFORM,TNS_PC_IDN_MAKE_TRANSFORM, IDN_MakeTransformInit, IDN_MakeTransformDestroy, IDN_MakeTransformVisit, IDN_MakeTransformEval, tnsMakeTransformNode);
 }
 
-void laDriverRequestRebuild(){ MAIN.DriverNeedRebuild=1; }
-void laDriverRequestEval(){ MAIN.DriverNeedEval=1; }
+void laDriverRequestRebuild(){ MAIN.Drivers->NeedRebuild=1; }
+void laDriverRequestEval(){ MAIN.Drivers->NeedEval=1; }
 
 int la_RunDrivers(){
-    MAIN.MappingNeedEval = 0;
-    for(laListItemPointer*lip=MAIN.DriverEval.pFirst;lip;lip=lip->pNext){
+    MAIN.Drivers->NeedEval = 0;
+    for(laListItemPointer*lip=MAIN.Drivers->Eval.pFirst;lip;lip=lip->pNext){
         laBaseNode* n=lip->p; n->Type->Eval(n);
     }
     return 1;
 }
 int la_RebuildDriverEval(){
-    MAIN.MappingNeedRebuild = 0;
-    while(lstPopPointer(&MAIN.DriverEval));
+    MAIN.Drivers->NeedRebuild = 0;
+    while(lstPopPointer(&MAIN.Drivers->Eval));
     laListHandle pending={0};
-    for(laRackPage* dp=MAIN.DriverPages.pFirst;dp;dp=dp->Item.pNext){
+    for(laRackPage* dp=MAIN.Drivers->Pages.pFirst;dp;dp=dp->Item.pNext){
         for(laNodeRack* ir=dp->Racks.pFirst;ir;ir=ir->Item.pNext){
             for(laBaseNode*bn=ir->Nodes.pFirst;bn;bn=bn->Item.pNext){ lstAppendPointer(&pending,bn); bn->Eval=0; }
         }
@@ -236,8 +236,8 @@ int la_RebuildDriverEval(){
     laBaseNode*n;int result=LA_DAG_FLAG_PERM; laListItemPointer*NextLip;
     for(laListItemPointer*lip=pending.pFirst;lip;lip=NextLip){ n=lip->p; NextLip=lip->pNext;
         if(n->Eval&LA_DAG_FLAG_PERM) continue;
-        result=n->Type->Visit(n,&MAIN.DriverEval); if(result==LA_DAG_FLAG_ERR){ while(lstPopPointer(&pending)); break; }
+        result=n->Type->Visit(n,&MAIN.Drivers->Eval); if(result==LA_DAG_FLAG_ERR){ while(lstPopPointer(&pending)); break; }
     }
-    if(result==LA_DAG_FLAG_ERR){ while(lstPopPointer(&MAIN.DriverEval)); return LA_DAG_FLAG_ERR; }
+    if(result==LA_DAG_FLAG_ERR){ while(lstPopPointer(&MAIN.Drivers->Eval)); return LA_DAG_FLAG_ERR; }
     return LA_DAG_FLAG_PERM;
 }

+ 18 - 19
source/lagui/resources/la_widgets.c

@@ -1240,7 +1240,9 @@ void la_NodeSocketDraw(laUiItem *ui, int h){
 }
 void la_HeightAdjusterDraw(laUiItem *ui, int h){
     laBoxedTheme *bt = (*ui->Type->Theme);
-    tnsDrawStringAuto("☰", laThemeColor(bt,LA_BT_TEXT), ui->L-1000, ui->R+1000, ui->U, LA_TEXT_ALIGN_CENTER);
+    char* str="☰";
+    if(ui->State==LA_BT_ACTIVE){ str="🡙"; }
+    tnsDrawStringAuto(str, laThemeColor(bt,LA_BT_TEXT), ui->L-1000, ui->R+1000, ui->U, LA_TEXT_ALIGN_CENTER);
 }
 void la_RawPropDraw(laUiItem *ui, int h){
     laBoxedTheme *bt = (*ui->Type->Theme);
@@ -1776,7 +1778,7 @@ int OPMOD_IntArrayHorizon(laOperator *a, laEvent *e){
         int GX = e->x, GY = e->y;
         ui->PP.LastIndex = la_DetectColumn(ui, e->x, Len);
         laLocalToWindow(a, a->ToPanel, &GX, &GY);
-        laPanel *p = laEnablePropertyPanel(a->ToPanel, a, 0, 0, &ui->PP, GX, GX + 150, GY, 600, 200, e);
+        laPanel *p = laEnablePropertyPanel(a->ToPanel, a, 0, 0, 0, &ui->PP, GX, GX + 150, GY, 600, 200, e);
         return LA_RUNNING;
     }
 
@@ -1895,7 +1897,7 @@ int OPMOD_FloatArrayHorizon(laOperator *a, laEvent *e){
         int GX = e->x, GY = e->y;
         ui->PP.LastIndex = la_DetectColumn(ui, e->x, laGetArrayLength(&ui->PP));
         laLocalToWindow(a, a->ToPanel, &GX, &GY);
-        laPanel *p = laEnablePropertyPanel(a->ToPanel, a, 0, 0, &ui->PP, GX, GX + 150, GY, 600, 200, e);
+        laPanel *p = laEnablePropertyPanel(a->ToPanel, a, 0, 0, 0, &ui->PP, GX, GX + 150, GY, 600, 200, e);
         return LA_RUNNING;
     }
 
@@ -1939,7 +1941,7 @@ int OPMOD_FloatColor(laOperator *a, laEvent *e){
     if (e->Type == LA_R_MOUSE_DOWN){
         int GX = e->x, GY = e->y;
         laLocalToWindow(a, a->ToPanel, &GX, &GY);
-        laPanel *p = laEnablePropertyPanel(a->ToPanel, a, 0, 0, &ui->PP, GX, GX + 150, GY, 600, 200, e);
+        laPanel *p = laEnablePropertyPanel(a->ToPanel, a, 0, 0, 0, &ui->PP, GX, GX + 150, GY, 600, 200, e);
         return LA_RUNNING;
     }
 
@@ -2080,7 +2082,7 @@ int OPMOD_EnumSelector(laOperator *a, laEvent *e){
             ui->State = LA_UI_ACTIVE;
             laLocalToWindow(a, a->ToPanel, &GX, &GY);
             laLocalToWindow(a, a->ToPanel, &GR, &t);
-            laEnablePropertyPanel(a->ToPanel, a, 0, la_DefaultEnumPanel, &ui->PP, GX, GR, GY, 200, 300, e);
+            laEnablePropertyPanel(a->ToPanel, a, 0, la_DefaultEnumPanel, 0, &ui->PP, GX, GR, GY, 200, 300, e);
             laRedrawCurrentPanel();
         }elif(IsCycle){
             laEnumItem* ei=laGetEnumArrayIndexed(&ui->PP, ArrTarget);
@@ -2102,7 +2104,7 @@ int OPMOD_EnumSelector(laOperator *a, laEvent *e){
     if(e->Type==LA_R_MOUSE_DOWN){
         int GX = e->x, GY = e->y;
         laLocalToWindow(a, a->ToPanel, &GX, &GY);
-        laPanel *p = laEnablePropertyPanel(a->ToPanel, a, 0, 0, &ui->PP, GX, GX + 150, GY, 600, 200, e);
+        laPanel *p = laEnablePropertyPanel(a->ToPanel, a, 0, 0, 0, &ui->PP, GX, GX + 150, GY, 600, 200, e);
         return LA_RUNNING;
     }
 
@@ -2319,7 +2321,7 @@ int OPMOD_SingleLineString(laOperator *a, laEvent *e){
         int GX = e->x, GY = e->y;
         ui->PP.LastIndex = la_DetectColumn(ui, e->x, laGetArrayLength(&ui->PP));
         laLocalToWindow(a, a->ToPanel, &GX, &GY);
-        laPanel *p = laEnablePropertyPanel(a->ToPanel, a, ui->State==LA_UI_EDITING?&ui->ExtraPP:0, 0, &ui->PP, GX, GX + 150, GY, 600, 200, e);
+        laPanel *p = laEnablePropertyPanel(a->ToPanel, a, ui->State==LA_UI_EDITING?&ui->ExtraPP:0, 0, 0, &ui->PP, GX, GX + 150, GY, 600, 200, e);
         return LA_RUNNING;
     }
 
@@ -2406,7 +2408,7 @@ int OPMOD_MultiString(laOperator *a, laEvent *e){
         int GX = e->x, GY = e->y;
         ui->PP.LastIndex = la_DetectColumn(ui, e->x, laGetArrayLength(&ui->PP));
         laLocalToWindow(a, a->ToPanel, &GX, &GY);
-        laPanel *p = laEnablePropertyPanel(a->ToPanel, a, ui->State==LA_UI_ACTIVE?&ui->ExtraPP:0, 0, &ui->PP, GX, GX + 150, GY, 600, 200, e);
+        laPanel *p = laEnablePropertyPanel(a->ToPanel, a, ui->State==LA_UI_ACTIVE?&ui->ExtraPP:0, 0, 0, &ui->PP, GX, GX + 150, GY, 600, 200, e);
         return LA_RUNNING;
     }
 
@@ -2521,12 +2523,8 @@ int OPMOD_Collection(laOperator *a, laEvent *e){
         laUiList* uil;
         for (uil = ui->Subs.pFirst; uil; uil = uil->Item.pNext) {
             if (laIsInBound(e->x, e->y, uil->L, uil->R, uil->U, uil->B)) {
-                //laSetActiveInstance(ui->PP.LastPs->p, ui->PP.LastPs->UseInstance, uil->Instance);
-                laInvoke(a, "LA_file_dialog", e, 0, 0, 0);
-                uit->Ptr1 = uil->Instance;
-                laRedrawCurrentPanel();
-                laConfirmInt(a, 0, LA_CONFIRM_DATA);
-                return LA_RUNNING;
+                ui->PP.EndInstance=uil->Instance; lx=e->x, ly=e->y; laLocalToWindow(a,a->ToPanel,&lx,&ly);
+                laEnablePropertyPanel(a->ToPanel,a,0,0,laui_IdentifierOnly,&ui->PP,lx,lx,ly,0,0,e);
             }
         }
         return LA_RUNNING;
@@ -2704,16 +2702,17 @@ int OPMOD_HeightAdjuster(laOperator *a, laEvent *e){
 
     if ((!uit->Dragging) && (!laIsInUiItem(ui, e->x, e->y))){ return LA_FINISHED_PASS; }
 
-    if (e->Type == LA_L_MOUSE_DOWN){
-        uit->Dragging=1; uit->LastY=e->y; uit->On=0;
+    if (e->Type == LA_L_MOUSE_DOWN || e->Type==LA_R_MOUSE_DOWN){
+        uit->Dragging=1; uit->LastY=e->y; uit->On=0; uit->TargetIndexVali=e->Type==LA_L_MOUSE_DOWN?(ui->State==LA_BT_ACTIVE?2:1):2; uit->HeightCoeff=1;
         return LA_RUNNING;
     }
+    if(e->Type==LA_L_MOUSE_UP && uit->HeightCoeff){ ui->State=(ui->State==LA_BT_ACTIVE)?LA_BT_NORMAL:LA_BT_ACTIVE; laRedrawCurrentPanel(); }
     if (uit->Dragging && e->Type == LA_MOUSEMOVE){
-        int diff=e->y-uit->LastY; diff=((real)diff+0.5)/(ui->B-ui->U);
-        if(diff!=uit->On){ int d=diff-uit->On; uit->On=diff; while(d){ laSetInt(&ui->PP, d>0?1:-1); d-=(d>0?1:-1); }; laRecalcCurrentPanel(); }
+        int diff=e->y-uit->LastY; diff=((real)diff+0.5)/(ui->B-ui->U); int delta=uit->TargetIndexVali;
+        if(diff!=uit->On){ uit->HeightCoeff=0; int d=diff-uit->On; uit->On=diff; while(d){ laSetInt(&ui->PP, d>0?delta:-delta); d-=(d>0?1:-1); }; laRecalcCurrentPanel(); }
         return LA_RUNNING;
     }
-    if (uit->Dragging && (e->Type == LA_L_MOUSE_UP || e->Type == LA_R_MOUSE_DOWN || e->key == LA_KEY_ESCAPE)){
+    if (uit->Dragging && (e->Type == LA_L_MOUSE_UP || e->Type == LA_R_MOUSE_UP || e->key == LA_KEY_ESCAPE)){
         uit->Dragging=0; return LA_RUNNING_PASS;
     }
 

+ 3 - 2
source/lagui/resources/la_widgets_viewers.c

@@ -514,7 +514,8 @@ void la_3DViewInit(laUiItem *ui){
     e->ParentUi = ui;
 
     tnsVector3d pos={-15, -20, 7};
-    e->ViewingCamera = tnsCreateCamera(0, "VIEWING_CAMERA", rad(50), LA_COLOR3(pos), rad(70), 0, rad(-40), 25);
+    tnsCamera* c= tnsCreateCamera(0, "VIEWING_CAMERA", rad(50), LA_COLOR3(pos), rad(70), 0, rad(-40), 25);
+    memAssignRef(e,&e->ViewingCamera,c);
     tnsVector3d target={0,0,0}; tnsVector3d up={0,0,1};
     tnsLookAt(e->ViewingCamera, target, up);
     e->ViewingCamera->FocusDistance=tnsDist3dv(pos,target);
@@ -537,7 +538,7 @@ void la_3DViewDestroy(laUiItem *ui){
     tnsDelete2DOffscreen(e->OffScr);
     tnsDelete2DOffscreen(e->OffScrShadow);
     
-    tnsDestroyObject(e->ViewingCamera);
+    //XXX: cause breaks tnsDestroyObject(e->ViewingCamera);
 
     memFree(e);
 }