*/}}
Browse Source

Math, user pref saving/autoload, mapping saving

Yiming Wu 2 years ago
parent
commit
d3cd82c0f6

+ 8 - 6
source/lagui/la_data.c

@@ -2029,8 +2029,8 @@ int laValidateProperties(){
 //==================================[RW]
 
 void la_GetWorkingDirectoryInternal(){
-    char mbuf[2048];
-    getcwd(mbuf,2048);
+    char mbuf[2048]; getcwd(mbuf,2048);
+    int len=strlen(mbuf);if(mbuf[len]!='/'){ mbuf[len]='/'; mbuf[len+1]=0; }
     strSafeSet(&MAIN.WorkingDirectory, mbuf);
 }
 
@@ -3084,14 +3084,14 @@ int la_ExtractProp(laUDF *udf, laManagedUDF* mUDF, laPropPack *pp, void *ParentI
                     SubPP.RawThis = pp;
                     SubPS.p = subp;
                     SubPS.UseInstance = Instance;
-                    if (!subp->SubProp){ subp->SubProp = la_ContainerLookup(((laSubProp *)subp)->TargetID); }
-                    if (Parent && subp->PropertyType == LA_PROP_SUB){
+                    if (subp&&!subp->SubProp){ subp->SubProp = la_ContainerLookup(((laSubProp *)subp)->TargetID); }
+                    if (subp&& Parent && subp->PropertyType == LA_PROP_SUB){
                         if (!subp->UDFIsRefer && !IsExceptionNode){
                             ucn = la_AppendUDFContentNode(uci, &SubPP, la_Tell(udf));
                             result = la_ExtractProp(udf, mUDF, &SubPP, ThisDBInst, Mode, ucn);
                         }else{ result = la_ExtractFakeProp(udf);}
                     }else{
-                        result = IsExceptionNode ? result = la_ExtractFakeProp(udf) : la_ExtractProp(udf, mUDF, &SubPP, ThisDBInst, Mode, Parent);
+                        result = (IsExceptionNode||!subp) ? result = la_ExtractFakeProp(udf) : la_ExtractProp(udf, mUDF, &SubPP, ThisDBInst, Mode, Parent);
                     }
                     EStatus = result ? result : EStatus;
                 }
@@ -3112,6 +3112,8 @@ int la_ExtractProp(laUDF *udf, laManagedUDF* mUDF, laPropPack *pp, void *ParentI
 
                     if (Parent) uci = la_AppendUDFContentInstance(Parent, la_Tell(udf));
 
+                    printf("add pc %s\n",pc->Identifier);
+
                     int replaced=0;
                     if (udf){
                         RealSize = RealSize ? RealSize : p->SubProp->NodeSize;
@@ -3236,7 +3238,7 @@ int la_RematchPointers(int Mode){
 
     la_ExecutePtrSyncCommand(Mode);
 
-    while(uprd=lstPopItem(&MAIN.PostReadNodes)){ uprd->Func(uprd->Instance); free(uprd); }
+    while(uprd=lstPopItem(&MAIN.PostReadNodes)){ uprd->Func(uprd->Instance); memFree(uprd); }
     while(inst=lstPopPointer(&MAIN.RenewHyper2s)){ memMakeHyperData(memGetHead(inst,0)); }
 }
 

+ 40 - 12
source/lagui/la_interface.h

@@ -219,7 +219,7 @@ STRUCTURE(laNodeInSocket){
 };
 
 NEED_STRUCTURE(laBaseNodeType);
-NEED_STRUCTURE(laDriverPage);
+NEED_STRUCTURE(laRackPage);
 
 typedef laBaseNodeType* (*laGetBaseNodeTypeF)(char* str);
 
@@ -380,7 +380,6 @@ STRUCTURE(LA){
     //display:
     real FloatingAlpha;
     int SolidShadowLength;
-    int TrackShadowLength;
     int EnableAnimation;
     real AnimationSpeed;
     real PanelAnimationSpeed;
@@ -397,11 +396,12 @@ STRUCTURE(LA){
 
     laListHandle MediaFiles;
 
-    laListHandle InputMappingRacks;
+    laRackPage* CurrentInputMappingPage;
+    laListHandle InputMappingPages;
     laListHandle InputMappingEval;
     int MappingNeedEval,MappingNeedRebuild;
 
-    laDriverPage* CurrentDriverPage;
+    laRackPage* CurrentDriverPage;
     laListHandle DriverPages;
     laListHandle DriverEval;
     int DriverNeedEval,DriverNeedRebuild;
@@ -900,6 +900,11 @@ STRUCTURE(laWidget){
 #define LA_UI_FLAGS_PLAIN     (LA_UI_FLAGS_NO_DECAL|LA_UI_FLAGS_NO_EVENT)
 #define LA_TEXT_ALIGN (LA_TEXT_ALIGN_LEFT|LA_TEXT_ALIGN_CENTER|LA_TEXT_ALIGN_RIGHT|LA_TEXT_ALIGN_AUTO)
 
+#define LA_UI_SOCKET_LABEL_N LA_TEXT_ALIGN_LEFT
+#define LA_UI_SOCKET_LABEL_S LA_TEXT_ALIGN_RIGHT
+#define LA_UI_SOCKET_LABEL_W LA_TEXT_ALIGN_CENTER
+#define LA_UI_SOCKET_LABEL_E LA_TEXT_ALIGN_AUTO
+
 extern laWidget* LA_WIDGET_FIXED_GROUP;
 extern laWidget* LA_WIDGET_TAB;
 extern laWidget* LA_WIDGET_COLLECTION;
@@ -1217,17 +1222,15 @@ laBaseNode* la_CreateInputMapperNode(laNodeRack* ir, laBaseNodeType* NodeType);
 #define LA_NODE_TYPE_DRIVER 1
 
 laPropContainer* laget_BaseNodeType(laBaseNode* bn);
-void laRegisterNode(laBaseNodeType* type, laPropContainer* pc, laBaseNodeInitF init, laBaseNodeDestroyF destroy, laBaseNodeVisitF visit, laBaseNodeEvalF eval, int nodesize);
+void laRegisterNode(laBaseNodeType* type, laPropContainer* pc, laBaseNodeInitF init, laBaseNodeDestroyF destroy, laBaseNodeVisitF visit, laBaseNodeEvalF eval, int nodesize, char* udf_string);
 void laSetExtraNodeFunctions(laUiDefineFunc AddInputNodes, laUiDefineFunc AddDriverNodes, laGetBaseNodeTypeF GetInputNodeType, laGetBaseNodeTypeF GetDriverNodeType);
 
 #define LA_BASE_NODE_HEADER(uil,c,This)\
-    {laUiItem* _b=laBeginRow(uil,c,0,0);\
-    laShowHeightAdjuster(uil,c,This,"base.__gap",0);\
-    laShowItem(uil,c,This,"base.name")->Expand=1;\
-    laEndRow(uil,_b);}
+    {laSplitColumn(uil,c,0.2);laColumn*c1=laLeftColumn(c,1);c=laRightColumn(c,0);\
+    laShowHeightAdjuster(uil,c1,This,"base.__gap",0);}
 
 #define LA_IDN_REGISTER(a,pc,init,destroy,visit,eval,type)\
-    laRegisterNode(&a,pc,init,destroy,visit,eval,sizeof(type))
+    laRegisterNode(&a,pc,init,destroy,visit,eval,sizeof(type),"UDFRES_" #type);
 
 #define LA_SRC_AND_PARENT(socket)\
     ((socket)->Source && (socket)->Source->Parent)
@@ -1248,6 +1251,7 @@ extern laBaseNodeType LA_IDN_SWITCH;
 extern laBaseNodeType LA_IDN_COMBINE;
 extern laBaseNodeType LA_IDN_VALUES;
 extern laBaseNodeType LA_IDN_MATRIX;
+extern laBaseNodeType LA_IDN_MATH;
 
 extern laBaseNodeType TNS_IDN_TRANSFORM;
 extern laBaseNodeType TNS_IDN_MAKE_TRANSFORM;
@@ -1259,7 +1263,21 @@ extern laBaseNodeType TNS_IDN_MAKE_TRANSFORM;
 #define LA_MATRIX_NODE_OP_MUL 0
 #define LA_MATRIX_NODE_OP_INV 1
 
-STRUCTURE(laDriverPage){
+#define LA_MATH_NODE_OP_ADD 0
+#define LA_MATH_NODE_OP_SUB 1
+#define LA_MATH_NODE_OP_MUL 2
+#define LA_MATH_NODE_OP_DIV 3
+#define LA_MATH_NODE_OP_POW 4
+#define LA_MATH_NODE_OP_LOG 5
+#define LA_MATH_NODE_OP_SIN 6
+#define LA_MATH_NODE_OP_COS 7
+#define LA_MATH_NODE_OP_TAN 8
+#define LA_MATH_NODE_OP_ASIN 9
+#define LA_MATH_NODE_OP_ACOS 10
+#define LA_MATH_NODE_OP_ATAN 11
+#define LA_MATH_NODE_OP_ATAN2 12
+
+STRUCTURE(laRackPage){
     laListItem Item;
     laSafeString* Name;
     laListHandle Racks;
@@ -1338,11 +1356,18 @@ STRUCTURE(laMatrixNode){
     tnsMatrix44d Mat;
     int Operation;
 };
+STRUCTURE(laMathNode){
+    laBaseNode Base;
+    laNodeInSocket *InL,*InR;
+    laNodeOutSocket* Out,*OutInt;
+    real Value; int ValueI;
+    real ValueL, ValueR;
+    int Operation;
+};
 
 #define LA_INPUT_CONTROLLER_NODE_MODE_BTN 0
 #define LA_INPUT_CONTROLLER_NODE_MODE_AXIS 1
 
-
 void logPrintT(int Type, char* format, ...);
 void logPrint(char* format, ...);
 void logPrintNew(char* format, ...);
@@ -1491,6 +1516,9 @@ laUiType *la_RegisterUiType(const char *Identifer, int ForType, const char *UseO
 void laSetWindowCursor(int id);
 void laRenameWindow(laWindow* wnd, char* name);
 
+void laSaveUserPreferences();
+void laEnsureUserPreferences();
+
 int laGetReady();
 void laSetFrameCallbacks(laPreFrameF PreFrame, laPreDrawF PreDraw, laPostFrameF PostFrame);
 laWindow *laDesignWindow(int X, int Y, int W, int H);

+ 26 - 5
source/lagui/la_kernel.c

@@ -355,7 +355,6 @@ int laGetReady(){
     //display:
     MAIN.FloatingAlpha = 0.7;
     MAIN.SolidShadowLength = 20;
-    MAIN.TrackShadowLength = 30;
     MAIN.WireColorSlices = 16;
     MAIN.WireThickness = 5;
     MAIN.WireSaggyness = 5;
@@ -370,7 +369,6 @@ int laGetReady(){
     laAddResourceFolder(".");
     laAddResourceFolder("/home/yiming/Documents/sync/Projects/2022/nul5/build");
 
-
     //tns_RegisterResourcesForSoftwareRender();
     la_RegisterGeneralProps();
     la_RegisterInternalTemplates();
@@ -379,7 +377,6 @@ int laGetReady(){
     la_RegisterMainUiTypes();
     la_RegisterModellingOperators();
     la_RegisterInternalProps();
-    //laRegisterInternalNodes();
     la_RegisterWindowKeys();
 
     laFinalizeUiTemplates();
@@ -400,6 +397,8 @@ int laGetReady(){
 
     la_RegenerateWireColors();
 
+    laSaveProp("la.input_mapping_pages");
+
     logPrint("Initialization Completed\n");
 }
 
@@ -443,6 +442,22 @@ void laShutoff(){
     glXDestroyContext(MAIN.dpy,MAIN.glc);
 }
 
+void laSaveUserPreferences(){
+    char path[1024]; sprintf(path,"%s%s",MAIN.WorkingDirectory->Ptr,"preferences.udf");
+    laUDF* udf=laPrepareUDF(path);
+    laWriteProp(udf,"la.user_preferences");
+    laPackUDF(udf,0);
+}
+void laEnsureUserPreferences(){
+    char path[1024]; sprintf(path,"%s%s",MAIN.WorkingDirectory->Ptr,"preferences.udf");
+    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; }
+    laExtractUDF(udf,0,LA_UDF_MODE_OVERWRITE,0);
+    laCloseUDF(udf);
+}
+
+
 //MSG====================================================
 
 int la_IsThisSysWindow(laWindow *wnd, Window hwnd){
@@ -4051,10 +4066,16 @@ 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(pc==LA_PC_SOCKET_OUT){
-        laNodeOutSocket* s=ui->PP.EndInstance; s->RuntimeX=(ui->TL+ui->TR)/2; s->RuntimeY=(ui->TU+ui->TB)/2;
+        laNodeOutSocket* s=ui->PP.EndInstance; s->RuntimeX=(sl+sr)/2; s->RuntimeY=(su+sb)/2;
     }else{
-        laNodeInSocket* s=ui->PP.EndInstance;  s->RuntimeX=(ui->TL+ui->TR)/2; s->RuntimeY=(ui->TU+ui->TB)/2;
+        laNodeInSocket* s=ui->PP.EndInstance;  s->RuntimeX=(sl+sr)/2; s->RuntimeY=(su+sb)/2;
     }
 }
 int la_UpdateUiListRecursive(laUiList *uil, int U, int L, int R, int B, int Fast, laPanel *ParentPanel){

+ 2 - 2
source/lagui/la_tns_kernel.c

@@ -2717,8 +2717,8 @@ int tnsGetMonoFontAdvance(){
 }
 int tnsInvalidateFontCache(){
     tnsFont*f=FM->UsingFont;
-    for(int i=0;i<TNS_UNICODE_COUNT;i++){ if(f->characters){ free(f->characters[i]); f->characters[i]=0; } }
-    for(int i=0;i<TNS_MONO_COUNT;i++){ if(f->monocharacters){ free(f->monocharacters[i]); f->monocharacters[i]=0; } }
+    for(int i=0;i<TNS_UNICODE_COUNT;i++){ if(f->characters[i]){ free(f->characters[i]); f->characters[i]=0; } }
+    for(int i=0;i<TNS_MONO_COUNT;i++){ if(f->monocharacters[i]){ free(f->monocharacters[i]); f->monocharacters[i]=0; } }
     f->CurrentX=f->CurrentY=0;
     f->height = LA_RH*(MAIN.FontSize/2.0f+0.5f);
     int GenHeight=LA_RH*MAIN.FontSize;

+ 132 - 31
source/lagui/resources/la_nodes_basic.c

@@ -12,6 +12,7 @@ laBaseNodeType LA_IDN_SWITCH;
 laBaseNodeType LA_IDN_COMBINE;
 laBaseNodeType LA_IDN_VALUES;
 laBaseNodeType LA_IDN_MATRIX;
+laBaseNodeType LA_IDN_MATH;
 
 laPropContainer* LA_PC_IDN_GENERIC;
 laPropContainer* LA_PC_IDN_KEYBOARD;
@@ -23,6 +24,7 @@ laPropContainer* LA_PC_IDN_SWITCH;
 laPropContainer* LA_PC_IDN_COMBINE;
 laPropContainer* LA_PC_IDN_VALUES;
 laPropContainer* LA_PC_IDN_MATRIX;
+laPropContainer* LA_PC_IDN_MATH;
 
 #define LA_IDN_CONTROLLER_RESET_SOCKET(ns)\
     {ns->IntVal[0]=0; ns->Out->DataType=LA_PROP_INT; ns->Offset=0; ns->Out->Data=&ns->IntVal;}
@@ -73,10 +75,11 @@ int IDN_ControllerEval(laInputControllerNode* n){
 }
 void laui_ControllerNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laInputControllerNode*n=This->EndInstance;
-    laColumn* cl,*cr; laSplitColumn(uil,c,0.3); cl=laLeftColumn(c,0); cr=laRightColumn(c,0);
+    laColumn* cl,*cr; 
+    LA_BASE_NODE_HEADER(uil,c,This);
+    laSplitColumn(uil,c,0.3); cl=laLeftColumn(c,0); cr=laRightColumn(c,0);
 
     laUiItem* b=laBeginRow(uil,c,0,0);
-    laShowHeightAdjuster(uil,c,This,"base.__gap",0);
     laShowItem(uil,c,This,"base.name")->Expand=1;
     laShowItem(uil,c,This,"user_id");
     laEndRow(uil,b);
@@ -101,7 +104,7 @@ void laui_ControllerNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laC
 }
 
 void IDN_InputVisualizeInit(laInputVisualizerNode* n){
-    n->In=laCreateInSocket("in", 0);
+    n->In=laCreateInSocket("IN", 0);
     strSafeSet(&n->Base.Name,"Input Visualizer");
 }
 void IDN_InputVisualizeDestroy(laInputVisualizerNode* n){
@@ -131,9 +134,8 @@ int IDN_InputVisualizerEval(laInputVisualizerNode* n){
 }
 void laui_InputVisualizeNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laInputVisualizerNode*n=This->EndInstance;
-    laColumn* cl,*cr; laSplitColumn(uil,c,0.4); cl=laLeftColumn(c,1); cr=laRightColumn(c,0);
-
     LA_BASE_NODE_HEADER(uil,c,This);
+    laColumn* cl,*cr; laSplitColumn(uil,c,0.4); cl=laLeftColumn(c,1); cr=laRightColumn(c,0);
 
     laShowNodeSocket(uil,cl,This,"in",0);
     laUiItem* b2=laOnConditionThat(uil,cr,laEqual(laPropExpression(This,"in.data_type"),laIntExpression(LA_PROP_FLOAT)));{
@@ -157,7 +159,7 @@ void IDN_SplitDestroy(laSplitNode* n){
 }
 int IDN_SplitVisit(laSplitNode* n, laListHandle* l){
     LA_GUARD_THIS_NODE(n);
-    if(n->In->Source){ laBaseNode* sn=n->In->Source->Parent; LA_VISIT_NODE(sn); }
+    if(LA_SRC_AND_PARENT(n->In)){ laBaseNode* sn=n->In->Source->Parent; LA_VISIT_NODE(sn); }
     n->Base.Eval=LA_DAG_FLAG_PERM;
     lstAppendPointer(l, n);
     return LA_DAG_FLAG_PERM;
@@ -188,8 +190,9 @@ void laui_SplitNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn
     LA_BASE_NODE_HEADER(uil,c,This);
 
     laUiItem* b=laBeginRow(uil,c,0,0);
-    laShowNodeSocket(uil,c,This,"in",0);laShowSeparator(uil,c)->Expand=1;
+    laShowNodeSocket(uil,c,This,"in",0);
     laShowItemFull(uil,c,This,"array_length",LA_WIDGET_INT_PLAIN,0,0,0);
+    laShowLabel(uil,c,"🡲",0,0)->Expand=1;
     for(int i=0;i<8;i++){
         char* buf[128]; sprintf(buf,"out%d.out",i); laShowNodeSocket(uil,cr,This,buf,0);
     }
@@ -256,7 +259,7 @@ void laui_SwitchNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColum
 }
 
 void IDN_CombineInit(laCombineNode* n){
-    n->Out=laCreateOutSocket(n,"out",0);n->OutInt=laCreateOutSocket(n,"out_int",0);n->OutEnum=laCreateOutSocket(n,"out_enum",0); strSafeSet(&n->Base.Name,"Combine");
+    n->Out=laCreateOutSocket(n,"F",0);n->OutInt=laCreateOutSocket(n,"I",0);n->OutEnum=laCreateOutSocket(n,"E",0); strSafeSet(&n->Base.Name,"Combine");
     for(int i=0;i<8;i++){ char str[4]; sprintf(str,"%d",i); n->In[i].In=laCreateInSocket(n,str); }
     n->Out->Data=n->Values; n->OutInt->Data=n->ValuesI; n->OutEnum->Data=n->ValuesI;
     n->Out->DataType=LA_PROP_FLOAT|LA_PROP_ARRAY; n->OutInt->DataType=LA_PROP_INT|LA_PROP_ARRAY; n->OutEnum->DataType=LA_PROP_ENUM|LA_PROP_ARRAY;
@@ -301,13 +304,10 @@ void laui_CombineNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColu
     laEndRow(uil,b);
 
     b=laBeginRow(uil,c,0,0);
-    laShowSeparator(uil,c)->Expand=1; 
-    laShowLabel(uil,c,"E",0,0)->Flags|=LA_TEXT_ALIGN_RIGHT;
-    laShowNodeSocket(uil,c,This,"out_enum",0);
-    laShowLabel(uil,c,"I",0,0)->Flags|=LA_TEXT_ALIGN_RIGHT;
-    laShowNodeSocket(uil,c,This,"out_int",0);
-    laShowLabel(uil,c,"F",0,0)->Flags|=LA_TEXT_ALIGN_RIGHT;
-    laShowNodeSocket(uil,c,This,"out",0);
+    laUiItem* lu=laShowLabel(uil,c,"Combine 🡲",0,0); lu->Expand=1; lu->Flags|=LA_TEXT_ALIGN_RIGHT;
+    laShowNodeSocket(uil,c,This,"out_enum",0)->Flags|=LA_UI_SOCKET_LABEL_N;
+    laShowNodeSocket(uil,c,This,"out_int",0)->Flags|=LA_UI_SOCKET_LABEL_N;
+    laShowNodeSocket(uil,c,This,"out",0)->Flags|=LA_UI_SOCKET_LABEL_N;
     laEndRow(uil,b);
 }
 
@@ -335,9 +335,9 @@ int IDN_ValuesEval(laValuesNode* n){
 }
 void laui_ValuesNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); laValuesNode*n=This->EndInstance;
-    laColumn* cl,*cr; laSplitColumn(uil,c,0.4); cl=laLeftColumn(c,3); cr=laRightColumn(c,0);
     laUiItem*b,*b2;
     LA_BASE_NODE_HEADER(uil,c,This);
+    laColumn* cl,*cr; laSplitColumn(uil,c,0.4); cl=laLeftColumn(c,3); cr=laRightColumn(c,0);
 
     for(int i=0;i<8;i++){
         char* bufm[32]; sprintf(bufm,"mode%d",i); laShowItem(uil,cl,This,bufm);
@@ -358,7 +358,7 @@ void laui_ValuesNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColum
 
 void IDN_MatrixInit(laMatrixNode* n){
     strSafeSet(&n->Base.Name,"Matrix");
-    n->InL=laCreateInSocket("l",0); n->InR=laCreateInSocket("r",0); n->Out=laCreateOutSocket(n,"out",LA_PROP_FLOAT|LA_PROP_ARRAY);
+    n->InL=laCreateInSocket("l",0); n->InR=laCreateInSocket("r",0); n->Out=laCreateOutSocket(n,"MAT",LA_PROP_FLOAT|LA_PROP_ARRAY);
     n->Out->ArrLen=16; n->Out->Data=n->Mat;
 }
 void IDN_MatrixDestroy(laMatrixNode* n){
@@ -398,28 +398,98 @@ void laui_MatrixNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColum
 
     b=laBeginRow(uil,c,0,0);
     laShowNodeSocket(uil,c,This,"in_l",0); laShowNodeSocket(uil,c,This,"in_r",0); laShowItem(uil,c,This,"operation");
-    laShowSeparator(uil,c)->Expand=1; laShowNodeSocket(uil,c,This,"out",0);
+    laShowSeparator(uil,c)->Expand=1; laShowNodeSocket(uil,c,This,"out",0)->Flags|=LA_UI_SOCKET_LABEL_W;
     laEndRow(uil,b);
 }
 
+void IDN_MathInit(laMathNode* n){
+    strSafeSet(&n->Base.Name,"Math");
+    n->InL=laCreateInSocket("l",0); n->InR=laCreateInSocket("r",0);
+    n->Out=laCreateOutSocket(n,"F",LA_PROP_FLOAT); n->OutInt=laCreateOutSocket(n,"I",LA_PROP_INT);
+    n->Out->Data=&n->Value; n->OutInt->Data=&n->ValueI;
+}
+void IDN_MathDestroy(laMathNode* n){
+    strSafeDestroy(&n->Base.Name); laDestroyInSocket(n->InL); laDestroyInSocket(n->InR); laDestroyOutSocket(n->Out); laDestroyOutSocket(n->OutInt);
+}
+int IDN_MathVisit(laMathNode* n, laListHandle* l){
+    LA_GUARD_THIS_NODE(n);
+    if(LA_SRC_AND_PARENT(n->InL)){ laBaseNode* bn=n->InL->Source->Parent; LA_VISIT_NODE(bn); }
+    if(LA_SRC_AND_PARENT(n->InR)){ laBaseNode* bn=n->InR->Source->Parent; LA_VISIT_NODE(bn); }
+    n->Base.Eval=LA_DAG_FLAG_PERM;
+    lstAppendPointer(l, n);
+    return LA_DAG_FLAG_PERM;
+}
+#define LA_GET_SRC_AS_FLOAT(var, socket)\
+    {if(socket->Source->DataType&LA_PROP_FLOAT) var=*((real*)socket->Source->Data);\
+    if(socket->Source->DataType&(LA_PROP_INT|LA_PROP_ENUM)) var=*((int*)socket->Source->Data);}
+int IDN_MathEval(laMathNode* n){
+    real vl=n->ValueL;real vr=n->ValueR;
+    int hasl=LA_SRC_AND_PARENT(n->InL),hasr=LA_SRC_AND_PARENT(n->InR);
+    if(hasl){ LA_GET_SRC_AS_FLOAT(vl,n->InL) } if(hasr){ LA_GET_SRC_AS_FLOAT(vr,n->InR) }
+    switch(n->Operation){
+    case LA_MATH_NODE_OP_ADD: default: n->Value=vl+vr; break;
+    case LA_MATH_NODE_OP_SUB: n->Value=vl-vr; break;
+    case LA_MATH_NODE_OP_MUL: n->Value=vl*vr; break;
+    case LA_MATH_NODE_OP_DIV: if(vr)n->Value=vl/vr;else n->Value=0; break;
+    case LA_MATH_NODE_OP_POW: n->Value=pow(vl,vr); break;
+    case LA_MATH_NODE_OP_LOG: n->Value=log2(vl)/log2(vr); break;
+    case LA_MATH_NODE_OP_SIN: n->Value=sin(vl); break;
+    case LA_MATH_NODE_OP_COS: n->Value=cos(vl); break;
+    case LA_MATH_NODE_OP_TAN: n->Value=tan(vl); break;
+    case LA_MATH_NODE_OP_ASIN: n->Value=asin(vl); break;
+    case LA_MATH_NODE_OP_ACOS: n->Value=acos(vl); break;
+    case LA_MATH_NODE_OP_ATAN: n->Value=atan(vl); break;
+    case LA_MATH_NODE_OP_ATAN2: n->Value=atan2(vl,vr); break;
+    }
+    n->ValueI=(int)n->Value;
+    return 1;
+}
+void laui_MathNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil); laMathNode*n=This->EndInstance;
+    laColumn* cl,*cr; laSplitColumn(uil,c,0.4); cl=laLeftColumn(c,3); cr=laRightColumn(c,0);
+    laUiItem*b,*b2;
+    LA_BASE_NODE_HEADER(uil,c,This);
+
+    b=laBeginRow(uil,c,0,0);
+    laShowNodeSocket(uil,c,This,"in_l",0); b=laOnConditionThat(uil,c,laNot(laPropExpression(This,"in_l.source")));{ laShowItem(uil,c,This,"vl"); }laEndCondition(uil,b);
+    laShowSeparator(uil,c)->Expand=1; laShowItem(uil,c,This,"operation");
+    laEndRow(uil,b);
+    b=laBeginRow(uil,c,0,0);
+    laShowNodeSocket(uil,c,This,"in_r",0); b=laOnConditionThat(uil,c,laNot(laPropExpression(This,"in_r.source")));{ laShowItem(uil,c,This,"vr");}laEndCondition(uil,b);
+    laShowSeparator(uil,c)->Expand=1;
+    laShowNodeSocket(uil,c,This,"out_int",0)->Flags|=LA_UI_SOCKET_LABEL_N;
+    laShowNodeSocket(uil,c,This,"out",0)->Flags|=LA_UI_SOCKET_LABEL_N;
+    laEndRow(uil,b);
+}
 
 
-int OPINV_AddInputMapperRack(laOperator* a, laEvent *e){
-    laNodeRack* pivot=a->This?a->This->EndInstance:0;
-    laNodeRack* ir=memAcquire(sizeof(laNodeRack));
+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");
+    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)); 
+    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"); }
+    return LA_FINISHED;
 
-    if(strSame(strGetArgumentString(a->ExtraInstructionsP,"before"),"true")){
-        if(pivot){ lstInsertItemBefore(&MAIN.InputMappingRacks,ir,pivot); }else{ lstPushItem(&MAIN.InputMappingRacks,ir); }
-    }else { if(pivot){ lstInsertItemAfter(&MAIN.InputMappingRacks,ir,pivot); }else{ lstAppendItem(&MAIN.InputMappingRacks,ir); } }
-    laNotifyUsers("la.input_racks");
-    
+}
+int OPINV_RebuildInputMapping(laOperator* a, laEvent *e){
+    laMappingRequestRebuild();
     return LA_FINISHED;
 }
 
 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_racks");
+    laNotifyUsers("la.input_mapping_pages");
     return bn;
 }
 void la_DestroyInputMapperNode(laBaseNode* bn){
@@ -443,6 +513,7 @@ int OPINV_AddInputMapperNode(laOperator* a, laEvent *e){
     elif(strSame(type, "COMBINE")){ la_CreateInputMapperNode(ir, &LA_IDN_COMBINE); }
     elif(strSame(type, "VALUES")){ la_CreateInputMapperNode(ir, &LA_IDN_VALUES); }
     elif(strSame(type, "MATRIX")){ la_CreateInputMapperNode(ir, &LA_IDN_MATRIX); }
+    elif(strSame(type, "MATH")){ la_CreateInputMapperNode(ir, &LA_IDN_MATH); }
     elif(MAIN.ExtraGetInputNodeType && (bnt=MAIN.ExtraGetInputNodeType(type))){ la_CreateInputMapperNode(ir, bnt); }
 
     return LA_FINISHED;
@@ -462,6 +533,7 @@ void laui_AddInputMapperNode(laUiList *uil, laPropPack *This, laPropPack *Extra,
     laShowItemFull(uil,c,This,"add_node_input",0,"type=COMBINE;text=Combine",0,0);
     laShowItemFull(uil,c,This,"add_node_input",0,"type=VALUES;text=Values",0,0);
     laShowLabel(uil,c,"Math:",0,0);
+    laShowItemFull(uil,c,This,"add_node_input",0,"type=MATH;text=Math",0,0);
     laShowItemFull(uil,c,This,"add_node_input",0,"type=MATRIX;text=Matrix",0,0);
     laShowLabel(uil,c,"Visualizations:",0,0);
     laShowItemFull(uil,c,This,"add_node_input",0,"type=VISUALIZER;text=Visualizer",0,0);
@@ -499,10 +571,11 @@ int laget_VisualizerArrayLength(laInputVisualizerNode* s){
     return s->In->ArrLen?s->In->ArrLen:1;
 }
 
-void laRegisterNode(laBaseNodeType* type, laPropContainer* pc, laBaseNodeInitF init, laBaseNodeDestroyF destroy, laBaseNodeVisitF visit, laBaseNodeEvalF eval, int nodesize){
+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*));
     type->Init = init; type->Destroy = destroy; type->Visit=visit; type->Eval=eval; type->NodeSize=nodesize; type->pc=pc;
     MAIN.NodeTypes[MAIN.NodeTypeNext]=type; MAIN.NodeTypeNext++;
+    la_UDFAppendSharedTypePointer(udf_string, type);
 }
 
 void la_AddValuesNodeEnum(laProp* p){
@@ -519,9 +592,11 @@ void la_RegisterInputMapperOperators(){
     laOperatorType *at;
     laEnumProp *ep;
 
-    laCreateOperatorType("LA_add_input_mapper_rack", "Add Rack", "Add a rack for input mapper nodes", 0,0,0,OPINV_AddInputMapperRack,0,'+',0);
+    laCreateOperatorType("LA_add_input_mapping_page", "Add Page", "Add a page for inpur mapping", 0,0,0,OPINV_AddInputMapperPage,0,'+',0);
+    laCreateOperatorType("LA_add_rack", "Add Rack", "Add a rack for nodes", 0,0,0,OPINV_AddNodesRack,0,'+',0);
     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);
 
     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);
@@ -534,6 +609,7 @@ void la_RegisterInputMapperOperators(){
     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);
+    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);
 
     pc=laAddPropertyContainer("la_input_controller_node", "Controller output", "Output controller values",0,laui_ControllerNode,sizeof(laInputControllerNode),0,0,1);
     LA_PC_IDN_CONTROLLER=pc;
@@ -671,6 +747,30 @@ void la_RegisterInputMapperOperators(){
     laAddEnumItemAs(p,"MUL", "Multiply", "L x R", LA_MATRIX_NODE_OP_MUL, 0);
     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;
+    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);
+    laAddSubGroup(pc,"in_l", "L","Left input","la_in_socket",0,0,0,offsetof(laMathNode, 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(laMathNode, InR),0,0,0,0,0,0,0,LA_UDF_SINGLE);
+    laAddSubGroup(pc,"out", "Out","Output value","la_out_socket",0,0,0,offsetof(laMathNode, Out),0,0,0,0,0,0,0,LA_UDF_SINGLE);
+    laAddSubGroup(pc,"out_int", "Out Int","Output value in Integer","la_out_socket",0,0,0,offsetof(laMathNode, OutInt),0,0,0,0,0,0,0,LA_UDF_SINGLE);
+    p=laAddEnumProperty(pc,"operation", "Operation", "Operation", 0,0,0,0,0,offsetof(laMathNode, Operation),0,0,0,0,0,0,0,0,0,0);
+    laAddEnumItemAs(p,"ADD", "Add", "L + R", LA_MATH_NODE_OP_ADD, 0);
+    laAddEnumItemAs(p,"SUB", "Subtract", "L - R", LA_MATH_NODE_OP_SUB, 0);
+    laAddEnumItemAs(p,"MUL", "Multiply", "L x R", LA_MATH_NODE_OP_MUL, 0);
+    laAddEnumItemAs(p,"DIV", "Divide", "L / R", LA_MATH_NODE_OP_DIV, 0);
+    laAddEnumItemAs(p,"POW", "Power", "pow(L,R)", LA_MATH_NODE_OP_POW, 0);
+    laAddEnumItemAs(p,"LOG", "Log", "log(L)base(R)", LA_MATH_NODE_OP_LOG, 0);
+    laAddEnumItemAs(p,"SIN", "Sine", "sin(L)", LA_MATH_NODE_OP_SIN, 0);
+    laAddEnumItemAs(p,"COS", "Cosine", "cos(L)", LA_MATH_NODE_OP_COS, 0);
+    laAddEnumItemAs(p,"TAN", "Tangent", "tan(L)", LA_MATH_NODE_OP_TAN, 0);
+    laAddEnumItemAs(p,"ASIN", "Arcsin", "asin(L)", LA_MATH_NODE_OP_ASIN, 0);
+    laAddEnumItemAs(p,"ACOS", "Arccosine", "acos(L)", LA_MATH_NODE_OP_ACOS, 0);
+    laAddEnumItemAs(p,"ATAN", "Arctangent", "atan(L)", LA_MATH_NODE_OP_ATAN, 0);
+    laAddEnumItemAs(p,"ATAN2", "Atan2", "atan2(L,R) where L or R can be zero", LA_MATH_NODE_OP_ATAN2, 0);
+
     LA_IDN_REGISTER(LA_IDN_CONTROLLER,LA_PC_IDN_CONTROLLER, IDN_ControllerInit, IDN_ControllerDestroy, IDN_ControllerVisit, IDN_ControllerEval, laInputControllerNode);
     LA_IDN_REGISTER(LA_IDN_VISUALIZER,LA_PC_IDN_VISUALIZER, IDN_InputVisualizeInit, IDN_InputVisualizeDestroy, IDN_InputVisualizeVisit, IDN_InputVisualizerEval, laInputVisualizerNode);
     LA_IDN_REGISTER(LA_IDN_SPLIT,LA_PC_IDN_SPLIT, IDN_SplitInit, IDN_SplitDestroy, IDN_SplitVisit, IDN_SplitEval, laSplitNode);
@@ -678,6 +778,7 @@ void la_RegisterInputMapperOperators(){
     LA_IDN_REGISTER(LA_IDN_COMBINE,LA_PC_IDN_COMBINE, IDN_CombineInit, IDN_CombineDestroy, IDN_CombineVisit, IDN_CombineEval, laCombineNode);
     LA_IDN_REGISTER(LA_IDN_VALUES,LA_PC_IDN_VALUES, IDN_ValuesInit, IDN_ValuesDestroy, IDN_ValuesVisit, IDN_ValuesEval, laValuesNode);
     LA_IDN_REGISTER(LA_IDN_MATRIX,LA_PC_IDN_MATRIX, IDN_MatrixInit, IDN_MatrixDestroy, IDN_MatrixVisit, IDN_MatrixEval, laMatrixNode);
+    LA_IDN_REGISTER(LA_IDN_MATH,LA_PC_IDN_MATH, IDN_MathInit, IDN_MathDestroy, IDN_MathVisit, IDN_MathEval, laMathNode);
 
 }
 
@@ -701,8 +802,8 @@ int la_RunInputMapping(){
 int la_RebuildInputMapping(){
     MAIN.MappingNeedRebuild = 0;
     while(lstPopPointer(&MAIN.InputMappingEval));
-    laListHandle pending={0};
-    for(laNodeRack* ir=MAIN.InputMappingRacks.pFirst;ir;ir=ir->Item.pNext){
+    laListHandle pending={0}; laRackPage* rp=MAIN.CurrentInputMappingPage; 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;

+ 6 - 0
source/lagui/resources/la_operators.c

@@ -538,6 +538,10 @@ int OPINV_UDFPropagate(laOperator *a, laEvent *e){
     return LA_FINISHED;
 }
 
+int OPINV_SaveUserPreferences(laOperator *a, laEvent *e){
+    laSaveUserPreferences(); return LA_FINISHED;
+}
+
 int OPCHK_TerminateProgram(laPropPack *This, laStringSplitor *Instructions){
     return 1;
 }
@@ -1988,6 +1992,8 @@ void la_RegisterBuiltinOperators(){
     laCreateOperatorType("LA_remove_resource_folder", "Remove Resource Folder", "Remove a resource folder entry",
                                0, 0, 0, OPINV_RemoveResourceFolder, 0, L'❌', LA_ACTUATOR_SYSTEM);
 
+    laCreateOperatorType("LA_save_user_preferences", "Save Preferences", "Save user preferences", 0, 0, 0, OPINV_SaveUserPreferences, 0, 0, LA_ACTUATOR_SYSTEM);
+
     laCreateOperatorType("LA_confirm", "Confirm", "Confirm The Statement", 0, 0, 0, OPINV_DoNothing, 0, L'✔', LA_ACTUATOR_SYSTEM)
         ->ExtraInstructions = "feedback=CONFIRM;";
     laCreateOperatorType("LA_cancel", "Cancel", "Ignore The Statement", 0, 0, 0, OPINV_DoNothing, 0, L'❌', LA_ACTUATOR_SYSTEM)

+ 13 - 9
source/lagui/resources/la_properties.c

@@ -110,12 +110,14 @@ void* laget_DetachedControllerFirst(void* unused,void* unused2){
 
 void laset_UiRowHeight(void* unused, int val){
     MAIN.UiRowHeight = val;
+    MAIN.ScaledUiRowHeight=MAIN.UiRowHeight;
     la_RefreshThemeColor(MAIN.CurrentTheme);
     tnsInvalidateFontCache();
     laRedrawAllWindows();
 }
 void laset_FontSize(void* unused, real val){
     MAIN.FontSize = val;
+    MAIN.ScaledUiRowHeight=MAIN.UiRowHeight;
     tnsInvalidateFontCache();
     laRedrawAllWindows();
 }
@@ -599,7 +601,10 @@ void lapost_Panel(laPanel *p){
 
     p->Refresh = LA_TAG_RECALC;
 }
-
+void lapost_UserPreferences(void* unused){
+    MAIN.ScaledUiRowHeight=MAIN.UiRowHeight;
+    tnsInvalidateFontCache();
+}
 
 //void laget_DetachedPropContainerID(laProp* p, char * result) {
 //	strcpy(result, p->Detached->Container->Identifier);
@@ -890,9 +895,9 @@ void la_RegisterInternalProps(){
             laSubGroupDetachable(sp, laget_DetachedControllerFirst, laget_ListNext);
             LA_PROP_CONTROLLER=sp;
             
-            laAddSubGroup(p, "input_racks", "Input Racks", "Input Mapping Racks","la_node_rack",0,0,0,-1,0,0,0,0,0,0,offsetof(LA,InputMappingRacks),0);
+            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_driver_page",0,0,0,offsetof(LA,CurrentDriverPage),0,0,0,0,0,0,offsetof(LA,DriverPages),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);
 
             laAddStringProperty(p, "identifier", "Identifier", "Identifier", 0, 0, 0, 0, 0, 0, 0, laget_MainIdentifier, 0, 0, LA_AS_IDENTIFIER|LA_READ_ONLY);
@@ -902,10 +907,10 @@ 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_driver_page", "Driver Page", "A page of driver nodes", 0, laui_IdentifierOnly, sizeof(laDriverPage), 0, 0, 1);{
-            laAddStringProperty(p, "name", "Name", "Name of the page", 0, 0, 0, 0, 1, offsetof(laDriverPage, 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(laDriverPage,Racks),0);
-            laAddOperatorProperty(p,"add_rack","Add Rack", "Add a rack into the page", "LA_add_driver_rack", '+', 0);
+        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);
+            laAddOperatorProperty(p,"add_rack","Add Rack", "Add a rack into the page", "LA_add_rack", '+', 0);
         }
 
         //p = laAddPropertyContainer("udf_fail_node", "UDF Failed Node", "Single Wild Data Block Reference", laui_UDFFailNodeItem, sizeof(laUDFFailNode), 0, 0, 0, 0, 0); {
@@ -978,7 +983,7 @@ void la_RegisterInternalProps(){
 
         // USER PREF ========================================================
 
-        p = laAddPropertyContainer("la_user_preference", "User Preference", "Kernel settings for LA main structure", L'⚙', 0, sizeof(LA), 0, 0, 2|LA_PROP_OTHER_ALLOC);{
+        p = laAddPropertyContainer("la_user_preference", "User Preference", "Kernel settings for LA main structure", L'⚙', 0, sizeof(LA), lapost_UserPreferences, 0, 2|LA_PROP_OTHER_ALLOC);{
             laAddFloatProperty(p, "idle_time", "Idle time", "Time out on no input to show tooltips", 0, 0, 0, 2.0, 0.3, 0.05, 0.75, 0, offsetof(LA, IdleTime), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
             laAddIntProperty(p, "top_framerate", "Top Framerate", "Framerate limiter for drawing the user interface", 0, 0, 0, 60, 25, 1, 60, 0, offsetof(LA, TopFramerate), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
             laAddIntProperty(p, "valuator_threshold", "Valuator Threshold", "Drag How Many Pixels Trigger A Change In Valuator", 0, 0, 0, 10, 1, 1, 3, 0, offsetof(LA, ValuatorThreshold), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
@@ -1001,7 +1006,6 @@ void la_RegisterInternalProps(){
 
             laAddFloatProperty(p, "floating_alpha", "Shadow Alpha", "Shadow Transparency For Floating Panels", 0, 0, 0, 1, 0, 0.01, 0.7, 0, offsetof(LA, FloatingAlpha), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
             laAddIntProperty(p, "solid_shadow_length", "Solid Shadow Length", "Solod Length For Floating Panels", 0, 0, 0, 40, 0, 1, 6, 0, offsetof(LA, SolidShadowLength), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-            laAddIntProperty(p, "track_shadow_length", "Track Shadow Length", "Solod Length For Displaying Tracks", 0, 0, 0, 100, 0, 1, 30, 0, offsetof(LA, TrackShadowLength), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
             laAddIntProperty(p, "wire_color_slices", "Wire color slices ", "How many slices of color to give to node sockets", 0, 0, 0, 32, 1, 1, 16, 0, offsetof(LA, WireColorSlices), 0, laset_WireColorSlices, 0, 0, 0, 0, 0, 0, 0, 0, 0);
             laAddFloatProperty(p, "wire_thickness", "Wire thickness", "How thick is a wire", 0, 0, 0, 10, 0.1, 0.5, 5, 0, offsetof(LA, WireThickness), 0, laset_WireThickness, 0, 0, 0, 0, 0, 0, 0, 0, 0);
             laAddFloatProperty(p, "wire_saggyness", "Wire saggyness", "How saggy is a wire", 0, 0, 0, 20, 0, 0.5, 5, 0, offsetof(LA, WireSaggyness), 0, laset_WireSaggyness, 0, 0, 0, 0, 0, 0, 0, 0, 0);

+ 29 - 16
source/lagui/resources/la_templates.c

@@ -1120,7 +1120,7 @@ void laui_LogItem(laUiList *uil, laPropPack *Base, laPropPack *OperatorInst, laC
 
 
 void laui_UserPreference(laUiList *uil, laPropPack *Base, laPropPack *OperatorInst, laColumn *ExtraColumns, int context){
-    laColumn* c = laFirstColumn(uil);
+    laColumn* c = laFirstColumn(uil),*cl,*cr; laSplitColumn(uil,c,0.5);cl=laLeftColumn(c,0);cr=laRightColumn(c,0);
     laUiList *muil;
     laColumn *mc, *mcl, *mcr, *mcll, *mclr;
     laUiItem *b, *bracket;
@@ -1130,6 +1130,8 @@ void laui_UserPreference(laUiList *uil, laPropPack *Base, laPropPack *OperatorIn
     //p->Show = 0;
     //p->FastUpdate = 1;
 
+    laShowItem(uil,cr,0,"LA_save_user_preferences");
+
     bracket = laMakeTab(uil, c, 0);{
         muil = laAddTabPage(bracket, "Display");{
             //muil->HeightCoeff = -1;
@@ -1419,34 +1421,45 @@ void laui_NodeRack(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn
     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_InputMapper(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
-    laColumn* c=laFirstColumn(uil),*cl, *cr;
-
+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,0,"la.input_racks",0,0,laui_NodeRack,0); hui->Expand=15; hui->Flags|=LA_UI_FLAGS_NO_DECAL;
+        laUiItem* hui=laShowItemFull(gu,gc,This,"racks",0,0,laui_NodeRack,0); hui->Expand=15; hui->Flags|=LA_UI_FLAGS_NO_DECAL;
     }
-    laShowItem(uil,c,0,"LA_add_input_mapper_rack");
+    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;
+            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);
+    }laEndCondition(uil,b2);
+    
+    laShowItemFull(uil,c,0,"la.input_mapping_pages",LA_WIDGET_COLLECTION_SINGLE,0,laui_RackPage,0);
 }
 
 void lauidetached_Drivers(laPanel* p){
     la_MakeDetachedProp(p, "la.driver_pages", "page");
 }
-
-void laui_DriverPage(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;
-    }
-    laShowItem(uil,c,This,"add_rack");
-}
 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);
@@ -1464,7 +1477,7 @@ void laui_Drivers(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *
         laShowItemFull(uil,cr,Extra,"page",LA_WIDGET_COLLECTION_SELECTOR,0,0,0);
     }laEndCondition(uil,b2);
     
-    laShowItemFull(uil,c,Extra,"page",LA_WIDGET_COLLECTION_SINGLE,0,laui_DriverPage,0);
+    laShowItemFull(uil,c,Extra,"page",LA_WIDGET_COLLECTION_SINGLE,0,laui_RackPage,1);
 }
 
 

+ 17 - 27
source/lagui/resources/la_tns_drivers.c

@@ -10,7 +10,7 @@ laPropContainer* TNS_PC_IDN_TRANSFORM;
 laPropContainer* TNS_PC_IDN_MAKE_TRANSFORM;
 
 void IDN_TransformInit(tnsTransformNode* n){
-    n->Mat=laCreateInSocket("mat",0); strSafeSet(&n->Base.Name,"Transform");
+    n->Mat=laCreateInSocket("MAT",0); strSafeSet(&n->Base.Name,"Transform");
 }
 void IDN_TransformDestroy(tnsTransformNode* n){
     laDestroyInSocket(n->Mat); strSafeDestroy(&n->Base.Name);
@@ -33,22 +33,21 @@ int IDN_TransformEval(tnsTransformNode* n){
 }
 void tnsui_TransformNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); tnsTransformNode*n=This->EndInstance;
-    laColumn* cl,*cr; laSplitColumn(uil,c,0.3); cl=laLeftColumn(c,0); cr=laRightColumn(c,0);
-
     LA_BASE_NODE_HEADER(uil,c,This);
+    laColumn* cl,*cr; laSplitColumn(uil,c,0.3); cl=laLeftColumn(c,0); cr=laRightColumn(c,0);
 
     laUiItem* b=laBeginRow(uil,cl,0,0);
-    laShowNodeSocket(uil,cl,This,"mat",0);
+    laShowNodeSocket(uil,cl,This,"mat",0)->Flags|=LA_UI_SOCKET_LABEL_E;
     laEndRow(uil,b);
 
     laShowItem(uil,cr,This,"target");
 }
 
 void IDN_MakeTransformInit(tnsMakeTransformNode* n){
-    n->Out=laCreateOutSocket(n,"mat",0); strSafeSet(&n->Base.Name,"Make Transform");
-    n->Loc=laCreateInSocket("loc",0); 
-    n->Rot=laCreateInSocket("rot",0);  n->Angle=laCreateInSocket("angle",0); n->UseRot[2]=1;
-    n->Sca=laCreateInSocket("scale",0);  n->UseSca=1;
+    n->Out=laCreateOutSocket(n,"MAT",0); strSafeSet(&n->Base.Name,"Make Transform");
+    n->Loc=laCreateInSocket("LOC",0); 
+    n->Rot=laCreateInSocket("ROT",0);  n->Angle=laCreateInSocket("ANGLE",0); n->UseRot[2]=1;
+    n->Sca=laCreateInSocket("SCALE",0);  n->UseSca=1;
 }
 void IDN_MakeTransformDestroy(tnsMakeTransformNode* n){
     laDestroyOutSocket(n->Out); strSafeDestroy(&n->Base.Name);
@@ -80,57 +79,47 @@ int IDN_MakeTransformEval(tnsMakeTransformNode* n){
 }
 void tnsui_MakeTransformNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); tnsMakeTransformNode*n=This->EndInstance;
-    laColumn* cl,*cr; laSplitColumn(uil,c,0.3); cl=laLeftColumn(c,0); cr=laRightColumn(c,0);
     laUiItem* b2, *rui;
-
     LA_BASE_NODE_HEADER(uil,c,This);
 
     laUiItem* b=laBeginRow(uil,c,0,0);
-    laShowNodeSocket(uil,c,This,"loc",0); laShowLabel(uil,c,"Loc",0,0); b2=laOnConditionThat(uil,c,laNot(laPropExpression(This,"loc.source")));{
+    laShowNodeSocket(uil,c,This,"loc",0)->Flags|=LA_UI_SOCKET_LABEL_E; b2=laOnConditionThat(uil,c,laNot(laPropExpression(This,"loc.source")));{
         laShowItem(uil,c,This,"use_loc")->Expand=1;
     }laEndCondition(uil,b2);
     laEndRow(uil,b);
 
     b=laBeginRow(uil,c,0,0);
-    laShowNodeSocket(uil,c,This,"rot",0); laShowLabel(uil,c,"Rot",0,0); b2=laOnConditionThat(uil,c,laNot(laPropExpression(This,"rot.source")));{
+    laShowNodeSocket(uil,c,This,"rot",0)->Flags|=LA_UI_SOCKET_LABEL_E; b2=laOnConditionThat(uil,c,laNot(laPropExpression(This,"rot.source")));{
         laShowItem(uil,c,This,"use_rot")->Expand=1;
     }laEndCondition(uil,b2);
     laEndRow(uil,b);
 
     b=laBeginRow(uil,c,0,0);
-    laShowNodeSocket(uil,c,This,"angle",0); laShowLabel(uil,c,"Angle",0,0); b2=laOnConditionThat(uil,c,laNot(laPropExpression(This,"angle.source")));{
+    laShowNodeSocket(uil,c,This,"angle",0)->Flags|=LA_UI_SOCKET_LABEL_E; b2=laOnConditionThat(uil,c,laNot(laPropExpression(This,"angle.source")));{
         laShowItem(uil,c,This,"use_angle")->Expand=1;
     }laEndCondition(uil,b2);
     laEndRow(uil,b);
 
     b=laBeginRow(uil,c,0,0);
-    laShowNodeSocket(uil,c,This,"sca",0); laShowLabel(uil,c,"Scale",0,0); b2=laOnConditionThat(uil,c,laNot(laPropExpression(This,"sca.source")));{
+    laShowNodeSocket(uil,c,This,"sca",0)->Flags|=LA_UI_SOCKET_LABEL_E; b2=laOnConditionThat(uil,c,laNot(laPropExpression(This,"sca.source")));{
         laShowItem(uil,c,This,"use_sca")->Expand=1;
     }laEndCondition(uil,b2);
     laEndRow(uil,b);
 
     b=laBeginRow(uil,c,0,0);
     laShowSeparator(uil,c)->Expand=1;;
-    laShowNodeSocket(uil,c,This,"out",0);
+    laShowNodeSocket(uil,c,This,"out",0)->Flags|=LA_UI_SOCKET_LABEL_W;
     laEndRow(uil,b);
 }
 
 
 int OPINV_AddDriverPage(laOperator* a, laEvent *e){
-    laDriverPage* dp=memAcquire(sizeof(laDriverPage));
+    laRackPage* dp=memAcquire(sizeof(laRackPage));
     strSafeSet(&dp->Name,"New Page");
     lstAppendItem(&MAIN.DriverPages, dp); MAIN.CurrentDriverPage=dp;
     laNotifyUsers("la.driver_pages");
     return LA_FINISHED;
 }
-int OPINV_AddDriverRack(laOperator* a, laEvent *e){
-    laDriverPage* dp=a->This?a->This->EndInstance:0; if(!dp) return LA_FINISHED;
-    laNodeRack* ir=memAcquire(sizeof(laNodeRack)); ir->RackType=LA_RACK_TYPE_DRIVER;
-    strSafeSet(&ir->Name,"New Rack");
-    lstAppendItem(&dp->Racks, ir);
-    laNotifyUsers("la.driver_pages");
-    return LA_FINISHED;
-}
 
 laBaseNode* la_CreateDriverNode(laNodeRack* ir, laBaseNodeType* NodeType){
     laBaseNode* bn=memAcquire(NodeType->NodeSize);
@@ -157,6 +146,7 @@ int OPINV_AddDriverNode(laOperator* a, laEvent *e){
     elif(strSame(type, "COMBINE")){ la_CreateDriverNode(ir, &LA_IDN_COMBINE); }
     elif(strSame(type, "VALUES")){ la_CreateDriverNode(ir, &LA_IDN_VALUES); }
     elif(strSame(type, "MATRIX")){ la_CreateDriverNode(ir, &LA_IDN_MATRIX); }
+    elif(strSame(type, "MATH")){ la_CreateDriverNode(ir, &LA_IDN_MATH); }
     elif(MAIN.ExtraGetDriverNodeType && (bnt=MAIN.ExtraGetDriverNodeType(type))){ la_CreateDriverNode(ir, bnt); }
 
     return LA_FINISHED;
@@ -174,8 +164,9 @@ void laui_AddDriverNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laCo
     laShowLabel(uil,c,"Visualizations:",0,0);
     laShowItemFull(uil,c,This,"add_node_driver",0,"type=VISUALIZER;text=Visualizer",0,0);
     laShowLabel(uil,c,"Math:",0,0);
-    laShowItemFull(uil,c,This,"add_node_driver",0,"type=MAKE_TRANSFORM;text=Make Transform",0,0);
+    laShowItemFull(uil,c,This,"add_node_driver",0,"type=MATH;text=Math",0,0);
     laShowItemFull(uil,c,This,"add_node_driver",0,"type=MATRIX;text=Matrix",0,0);
+    laShowItemFull(uil,c,This,"add_node_driver",0,"type=MAKE_TRANSFORM;text=Make Transform",0,0);
     laShowLabel(uil,c,"Drivers:",0,0);
     laShowItemFull(uil,c,This,"add_node_driver",0,"type=TRANSFORM;text=Transform Objects",0,0);
 }
@@ -196,7 +187,6 @@ void tns_RegisterNodes(){
     laEnumProp *ep;
 
     laCreateOperatorType("LA_add_driver_page", "New Page", "Add a driver page", 0,0,0,OPINV_AddDriverPage,0,'+',0);
-    laCreateOperatorType("LA_add_driver_rack", "Add Rack", "Add a rack for driver nodes", 0,0,0,OPINV_AddDriverRack,0,'+',0);
     at=laCreateOperatorType("LA_add_driver_node", "Add Node", "Add a input mapper node",0,0,0,OPINV_AddDriverNode,OPMOD_FinishOnData,'+',0);
     at->UiDefine=laui_AddDriverNode;
     laCreateOperatorType("LA_driver_rebuild", "Rebuild Drivers", "Rebuild drivers for evaluation",0,0,0,OPINV_RebuildDrivers,0,L'⭮',0);
@@ -238,7 +228,7 @@ int la_RebuildDriverEval(){
     MAIN.MappingNeedRebuild = 0;
     while(lstPopPointer(&MAIN.DriverEval));
     laListHandle pending={0};
-    for(laDriverPage* dp=MAIN.DriverPages.pFirst;dp;dp=dp->Item.pNext){
+    for(laRackPage* dp=MAIN.DriverPages.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; }
         }

+ 24 - 17
source/lagui/resources/la_widgets.c

@@ -157,6 +157,9 @@ int la_LabelHeight(laUiItem *ui){
     tnsStringGetDimension(ui->Display->Ptr, 0, 0, ui->TR-ui->TL, &rows, ui->Flags&LA_TEXT_MONO);
     return rows;
 }
+int la_SocketGetHeight(laUiItem *ui){
+    if(ui->Flags&(LA_UI_SOCKET_LABEL_N|LA_UI_SOCKET_LABEL_S))return 2; return 1;
+}
 
 int la_ValueGetMinWidth(laUiItem *ui){
     return LA_RH*4;
@@ -260,7 +263,7 @@ int la_ColorPickerGetMinWidth(laUiItem *ui){
     return LA_RH * 9;
 }
 int la_SocketGetMinWidth(laUiItem *ui){
-    return LA_RH;
+    if(ui->Flags&(LA_UI_SOCKET_LABEL_W|LA_UI_SOCKET_LABEL_E))return LA_RH*3; return LA_RH;
 }
 
 void la_SingleLineStringDrawSelection(laUiItem *ui, int Begin, int U, laBoxedTheme *bt, uint32_t *str, laStringEdit *se);
@@ -276,10 +279,8 @@ void la_CollectionSelectorDraw(laUiItem *ui, int h){
 
     tnsUseNoTexture();
     tnsColor4dv(laThemeColor(bt,ui->State));
-    tnsVertex2d(ui->L, ui->U);
-    tnsVertex2d(ui->R, ui->U);
-    tnsVertex2d(ui->R, ui->B);
-    tnsVertex2d(ui->L, ui->B);
+    tnsVertex2d(ui->L, ui->U); tnsVertex2d(ui->R, ui->U);
+    tnsVertex2d(ui->R, ui->B); tnsVertex2d(ui->L, ui->B);
     tnsPackAs(GL_TRIANGLE_FAN);
 
     CrossDraw = laGetActiveInstanceStrict(ui->PP.LastPs->p, ui->PP.LastPs->UseInstance) ? 1 : 0;
@@ -1216,24 +1217,30 @@ void la_NodeSocketDraw(laUiItem *ui, int h){
     char* label=0; char* icon;
     laProp* p=ui->PP.LastPs->p; laPropContainer* pc=la_EnsureSubTarget(p,0);
     real* ColorBkg=0,*ColorSocket;
-    if(pc==LA_PC_SOCKET_IN)   { laNodeInSocket*s=ui->PP.EndInstance; ColorSocket=laThemeColor(bt,LA_BT_TEXT);}
-    elif(pc==LA_PC_SOCKET_OUT){ laNodeOutSocket*s=ui->PP.EndInstance; ColorSocket=laThemeColor(bt,LA_BT_TEXT_ACTIVE); ColorBkg=laThemeColor(bt, LA_BT_ACTIVE); }
+    if(pc==LA_PC_SOCKET_IN)   { laNodeInSocket*s=ui->PP.EndInstance; ColorSocket=laThemeColor(bt,LA_BT_TEXT); ColorBkg=laThemeColor(bt, LA_BT_NORMAL); label=s->Label?s->Label->Ptr:""; }
+    elif(pc==LA_PC_SOCKET_OUT){ laNodeOutSocket*s=ui->PP.EndInstance; ColorSocket=laThemeColor(bt,LA_BT_TEXT_ACTIVE); ColorBkg=laThemeColor(bt, LA_BT_ACTIVE); label=s->Label?s->Label->Ptr:""; }
 
     int IsDisabled=ui->Flags&LA_UI_FLAGS_DISABLED;
 
-    if(ColorBkg){
-        tnsUseNoTexture();
-        tnsVertex2d(ui->L, ui->U); tnsVertex2d(ui->R, ui->U);
-        tnsVertex2d(ui->R, ui->B); tnsVertex2d(ui->L, ui->B);
-        tnsColor4dv(ColorBkg);
-        tnsPackAs(GL_TRIANGLE_FAN);
-    }
+    tnsUseNoTexture();
+    tnsVertex2d(ui->L, ui->U); tnsVertex2d(ui->R, ui->U);
+    tnsVertex2d(ui->R, ui->B); tnsVertex2d(ui->L, ui->B);
+    tnsColor4dv(ColorBkg);
+    tnsPackAs(GL_TRIANGLE_FAN);
+
+    int tl,tr,tu,sl,sr,su;
+    if(ui->Flags&LA_UI_SOCKET_LABEL_N){ tl=ui->L; tu=ui->U; tr=ui->R; sl=ui->L; sr=ui->R; su=ui->U+LA_RH; }
+    elif(ui->Flags&LA_UI_SOCKET_LABEL_S){ tl=ui->L; tu=ui->U+LA_RH; tr=ui->R; sl=ui->L; sr=ui->R; su=ui->U; }
+    elif(ui->Flags&LA_UI_SOCKET_LABEL_W){ tl=ui->L; tu=ui->U; tr=tl+LA_2RH; sl=ui->L+LA_2RH; sr=ui->R; su=ui->U; }
+    elif(ui->Flags&LA_UI_SOCKET_LABEL_E){ tu=ui->U; tr=ui->R; tl=tr-LA_2RH; sl=ui->L; sr=tl; su=ui->U; }
+    else{ sl=ui->L; su=ui->U; sr=ui->R; label=0; }
 
-    tnsDrawStringAuto("⭗", IsDisabled?laThemeColor(bt,LA_BT_TEXT|LA_UI_DISABLED):ColorSocket, ui->L, ui->R, ui->U, LA_TEXT_ALIGN_CENTER);
+    tnsDrawStringAuto("⭗", IsDisabled?laThemeColor(bt,LA_BT_TEXT|LA_UI_DISABLED):ColorSocket, sl, sr, su, LA_TEXT_ALIGN_CENTER);
+    if(label) tnsDrawStringAuto(label, IsDisabled?laThemeColor(bt,LA_BT_TEXT|LA_UI_DISABLED):ColorSocket, tl, tr, tu, LA_TEXT_ALIGN_CENTER|LA_TEXT_MONO);
 }
 void la_HeightAdjusterDraw(laUiItem *ui, int h){
     laBoxedTheme *bt = (*ui->Type->Theme);
-    tnsDrawStringAuto("◸", laThemeColor(bt,LA_BT_BORDER), ui->L, ui->R, ui->U, LA_TEXT_ALIGN_CENTER);
+    tnsDrawStringAuto("☰", 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);
@@ -1518,7 +1525,7 @@ void la_RegisterUiTypesBasic(){
     _LA_UI_SYMBOL = la_RegisterUiType("LA_symbol_default", 0, 0, &_LA_THEME_BUTTON, la_SymbolDraw, la_SymbolGetHeight, 0, 0);
 
     LA_WIDGET_NODE_SOCKET->Type=
-    _LA_UI_NODE_SOCKET = la_RegisterUiType("LA_node_socket_default", 0, "LA_node_socket", &_LA_THEME_SOCKET, la_NodeSocketDraw, 0, la_GeneralUiInit, la_GeneralUiDestroy);
+    _LA_UI_NODE_SOCKET = la_RegisterUiType("LA_node_socket_default", 0, "LA_node_socket", &_LA_THEME_SOCKET, la_NodeSocketDraw, la_SocketGetHeight, la_GeneralUiInit, la_GeneralUiDestroy);
     _LA_UI_NODE_SOCKET->GetMinWidth=la_SocketGetMinWidth;
     
     LA_WIDGET_HEIGHT_ADJUSTER->Type=