*/}}
Browse Source

Basic action animation UI stuff.

YimingWu 1 year ago
parent
commit
93e9c8aa48
10 changed files with 377 additions and 128 deletions
  1. 97 19
      la_animation.c
  2. 16 1
      la_data.h
  3. 6 4
      la_interface.h
  4. 41 21
      la_kernel.c
  5. 2 2
      la_tns_kernel.c
  6. 57 46
      resources/la_modelling.c
  7. 39 8
      resources/la_properties.c
  8. 96 14
      resources/la_templates.c
  9. 18 9
      resources/la_widgets.c
  10. 5 4
      resources/la_widgets_viewers.c

+ 97 - 19
la_animation.c

@@ -27,8 +27,8 @@ laAction* laAnimiationNewAction(char* Name){
     if(!Name || !Name[0]){ Name="New Action"; }
     strSafeSet(&aa->Name,Name);
     aa->Length=5; aa->FrameCount=120;
-    lstAppendItem(&MAIN.Actions,aa);
-    laNotifyUsers("la.actions");
+    memAssignRef(MAIN.Animation,&MAIN.Animation->CurrentAction,aa);
+    lstAppendItem(&MAIN.Animation->Actions,aa); laNotifyUsers("la.animation.actions");
     return aa;
 }
 laAnimationChannel* laAnimationEnsureChannel(laAction* aa, void* hyper1, laProp* p){
@@ -69,19 +69,19 @@ void laAnimationStoreKeyValue(laAnimationChannel* ac, laAnimationKey* ak){
 }
 laAnimationKey* laAnimationInsertKeyFrame(laAction* aa, void* hyper1, laProp* p){
     laAnimationChannel* ac=laAnimationEnsureChannel(aa,hyper1,p); if(!ac || !ac->For) return;
-    int frame=aa->PlayHead/aa->Length*aa->FrameCount;
+    int frame=(aa->PlayHead*(real)aa->FrameCount+0.5);
     laAnimationKey* ak=laAnimationEnsureFrame(ac->DataSize,frame);
     laAnimationStoreKeyValue(ac,ak);
 }
 
 
 void laAnimationSetPlayStatus(int PlayStatus){
-    if(PlayStatus>3||PlayStatus<0) PlayStatus=0; MAIN.AnimationPlayStatus=PlayStatus;
-    if(PlayStatus) laRecordTime(&MAIN.AnimationTimeOrigin);
-    laNotifyUsers("la.animation_play_status");
+    if(PlayStatus>3||PlayStatus<0) PlayStatus=0; MAIN.Animation->PlayStatus=PlayStatus;
+    if(PlayStatus) laRecordTime(&MAIN.Animation->TimeOrigin);
+    laNotifyUsers("la.animation.play_status");
 }
 void laAnimationSetPlayHead(real time){
-    MAIN.AnimationPlayHead=time;
+    MAIN.Animation->PlayHead=time;
 }
 
 
@@ -93,7 +93,7 @@ int OPINV_AnimationPlayAction(laOperator *a, laEvent *e){
     char* str=strGetArgumentString(a->ExtraInstructionsP, "mode");
     int PlayStatus=LA_ANIMATION_STATUS_PAUSED;
     if(strSame(str,"forward")){ PlayStatus=LA_ANIMATION_STATUS_PLAY_FWD; }
-    elif(strSame(str,"reverse")){ if(MAIN.AnimationPlayHead>0) PlayStatus=LA_ANIMATION_STATUS_PLAY_REV; }
+    elif(strSame(str,"reverse")){ if(MAIN.Animation->PlayHead>0) PlayStatus=LA_ANIMATION_STATUS_PLAY_REV; }
     laAnimationSetPlayStatus(PlayStatus);
     return LA_FINISHED;
 }
@@ -103,33 +103,111 @@ int OPINV_AnimationResetTime(laOperator *a, laEvent *e){
 }
 
 void la_AnimationEvaluateActions(){
-    /* ehh well */
+    int any=0;
+    for(laAction* aa=MAIN.Animation->Actions.pFirst;aa;aa=aa->Item.pNext){
+        real UseTime=MAIN.Animation->PlayHead-aa->Offset;
+        int repeats=UseTime/aa->Length;
+        real remaining=UseTime-repeats*aa->Length;
+        while(remaining<0){ remaining+=aa->Length; }
+        if(aa->PlayMode==LA_ANIMATION_PLAY_MODE_REPEAT){ aa->PlayHead=remaining/aa->Length; }
+        elif(aa->PlayMode==LA_ANIMATION_PLAY_MODE_HOLD){ aa->PlayHead=(UseTime>aa->Length)?1.0:(UseTime<0?0:UseTime/aa->Length); }
+        elif(aa->PlayMode==LA_ANIMATION_PLAY_MODE_BOUNCE){ real t=remaining/aa->Length; aa->PlayHead=(repeats%2)?(1-t):t; }
+        any=1;
+    }
+    if(any) laNotifyUsers("la.animation");
 }
 
 void la_AnimationPreFrame(){
-    if(MAIN.AnimationPlayHead<0){
-        MAIN.AnimationPlayHead=0; laAnimationSetPlayStatus(0);
-        laNotifyUsers("la.animation_play_head");
+    if(MAIN.Animation->PlayHead<0){
+        MAIN.Animation->PlayHead=0; laAnimationSetPlayStatus(0);
+        laNotifyUsers("la.animation.play_head");
     }
-    if(MAIN.AnimationPlayStatus){
+    if(MAIN.Animation->PlayStatus){
         la_AnimationEvaluateActions();
     }
 }
 void la_AnimationPostFrame(){
     laTimeRecorder cur={0};
-    if(MAIN.AnimationPlayStatus){ laRecordTime(&cur);
-        real td=laTimeElapsedSecondsf(&cur,&MAIN.AnimationTimeOrigin);
-        laRecordTime(&MAIN.AnimationTimeOrigin);
-        if(MAIN.AnimationPlayStatus==LA_ANIMATION_STATUS_PLAY_REV){ td*=-1; }
-        MAIN.AnimationPlayHead+=td; laNotifyUsers("la.animation_play_head");
+    if(MAIN.Animation->PlayStatus){ laRecordTime(&cur);
+        real td=laTimeElapsedSecondsf(&cur,&MAIN.Animation->TimeOrigin);
+        laRecordTime(&MAIN.Animation->TimeOrigin);
+        if(MAIN.Animation->PlayStatus==LA_ANIMATION_STATUS_PLAY_REV){ td*=-1; }
+        MAIN.Animation->PlayHead+=td; laNotifyUsers("la.animation.play_head");
+    }
+}
+
+
+int LAMOD_AnimationActionsCanvas(laOperator *a, laEvent *e){
+    laUiItem *ui = a->Instance; laBoxedTheme *bt = (*ui->Type->Theme);
+    laCanvasExtra *ex = a->CustomData; laAction* aa=ui->PP.EndInstance;
+    if(!aa) LA_RUNNING_PASS;
+
+    return LA_RUNNING_PASS;
+}
+void la_AnimationActionCanvasInit(laUiItem *ui){
+    la_CanvasInit(ui);
+    laCanvasExtra* e=ui->Extra; e->LW=LA_RH*7; e->ShowLegend=1;
+}
+void la_AnimationActionDrawCanvas(laBoxedTheme *bt, laAction *aa, laUiItem* ui){
+    laCanvasExtra* e=ui->Extra; //laAction* aa=ui->PP.EndInstance;
+    int W, H; W = ui->R - ui->L; H = ui->B - ui->U; if (W<=0 || H<=0) return;
+    int ShowSide=(e->ShowLegend&&e->LW<W-2*LA_RH);
+    int ll=ui->L, lr=ll+(ShowSide?e->LW:0), tl=lr, tr=ui->R;
+
+    //if (!e->OffScr || e->OffScr->pColor[0]->Height != ui->B - ui->U || e->Be.OffScr->pColor[0]->Width != ui->R - ui->L){
+    //    if (e->OffScr) tnsDelete2DOffscreen(e->OffScr);
+    //    e->OffScr = tnsCreate2DOffscreen(GL_RGBA, W, H, MAIN.PanelMultisample, 1, 0);
+    //}
+    tnsUseNoTexture();
+
+    tnsColor4dv(laThemeColor(bt,LA_BT_BORDER));
+    tnsVertex2d(ui->L, ui->U); tnsVertex2d(ui->R, ui->U);
+    tnsVertex2d(ui->R, ui->B); tnsVertex2d(ui->L, ui->B);
+    tnsPackAs(GL_LINE_LOOP);
+
+    if(ShowSide){
+        tnsColor4dv(laThemeColor(bt,LA_BT_NORMAL));
+        tnsVertex2d(ll, ui->U); tnsVertex2d(lr, ui->U);
+        tnsVertex2d(lr, ui->B); tnsVertex2d(ll, ui->B);
+        tnsPackAs(GL_TRIANGLE_FAN);
+    }
+}
+void la_AnimationActionDrawOverlay(laUiItem *ui, int h){
+    laCanvasExtra *e = ui->Extra; laBoxedTheme *bt = (*ui->Type->Theme);
+
+    tnsUseImmShader();
+
+    if(MAIN.CurrentWindow->MaximizedUi!=ui){
+        tnsUseNoTexture();
+        tnsColor4dv(laThemeColor(bt,LA_BT_BORDER));
+        tnsVertex2d(ui->L, ui->U); tnsVertex2d(ui->R, ui->U);
+        tnsVertex2d(ui->R, ui->B); tnsVertex2d(ui->L, ui->B);
+        tnsPackAs(GL_LINE_LOOP);
     }
+
+    if (e->DrawCursor){
+        tnsColor4dv(laThemeColor(bt,LA_BT_TEXT));
+        int drawx=(e->DrawCursor==LA_CANVAS_CURSOR_CROSS)||(e->DrawCursor==LA_CANVAS_CURSOR_X);
+        int drawy=(e->DrawCursor==LA_CANVAS_CURSOR_CROSS)||(e->DrawCursor==LA_CANVAS_CURSOR_Y);
+        if(drawx) tnsVertex2d(e->OnX, ui->U); tnsVertex2d(e->OnX, ui->B);
+        if(drawy) tnsVertex2d(ui->L, e->OnY); tnsVertex2d(ui->R, e->OnY);
+        tnsPackAs(GL_LINES);
+        tnsFlush();
+    }
+
+    return;
 }
 
 
 void la_RegisterAnimationResources(){
     laPropContainer *pc; laProp *p; laOperatorType *at; laEnumProp *ep;
-    laCreateOperatorType("LA_animation_new_action", "New Action", "Add a new action",0,0,0,OPINV_AnimationNewAction,0,0,0);
+
+    laCreateOperatorType("LA_animation_new_action", "New Action", "Add a new action",0,0,0,OPINV_AnimationNewAction,0,U'🞦',0);
     laCreateOperatorType("LA_animation_set_play_status", "Set Play", "Set global animation player status",0,0,0,OPINV_AnimationPlayAction,0,0,0);
     laCreateOperatorType("LA_animation_reset_time", "Reset Time", "Reset Time",0,0,0,OPINV_AnimationResetTime,0,U'🡄',0);
+
+    laCanvasTemplate* ct=laRegisterCanvasTemplate("la_AnimationActionDrawCanvas", "la_animation_action", LAMOD_AnimationActionsCanvas, la_AnimationActionDrawCanvas, la_AnimationActionDrawOverlay, la_AnimationActionCanvasInit, la_CanvasDestroy);
+    pc = laCanvasHasExtraProps(ct,sizeof(laCanvasExtra),2); laAddIntProperty(pc, "height_coeff", "Ui Height", "Ui Height Coefficiency Entry", 0, 0, "Rows", 100, -100, 1, 0, 0, offsetof(laCanvasExtra, HeightCoeff), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+    
 }
 

+ 16 - 1
la_data.h

@@ -649,13 +649,28 @@ STRUCTURE(laDiffPost){
     laContainerUndoTouchedF Touched;
 };
 
+#define LA_ANIMATION_PLAY_MODE_REPEAT 0
+#define LA_ANIMATION_PLAY_MODE_HOLD   1
+#define LA_ANIMATION_PLAY_MODE_BOUNCE 2
+
+NEED_STRUCTURE(laAction);
+STRUCTURE(laAnimation){
+    real _PAD;
+    laAction*    CurrentAction;
+    laListHandle Actions;
+    real PlayHead;
+    int PlayStatus;
+    laTimeRecorder TimeOrigin;
+};
 STRUCTURE(laAction){
     laListItem Item;
     laSafeString* Name;
     laListHandle Channels;
     int FrameCount;
     real Length;
-    real PlayHead;
+    real PlayHead,Offset;
+    int PlayMode;
+    int Solo, Mute;
 };
 STRUCTURE(laAnimationChannel){
     laListItem Item;

+ 6 - 4
la_interface.h

@@ -289,6 +289,8 @@ STRUCTURE(laProgressDisplay){
     int Called,Shown;
 };
 
+NEED_STRUCTURE(laAnimation);
+
 STRUCTURE(LA){
     laListItem Hyper;
 
@@ -451,10 +453,7 @@ STRUCTURE(LA){
 
     laDiffPushEverythingF PushEverything;
 
-    laListHandle Actions;
-    real AnimationPlayHead;
-    int AnimationPlayStatus;
-    laTimeRecorder AnimationTimeOrigin;
+    laAnimation* Animation;
 
     real FontSize;
     real MarginSize;
@@ -906,6 +905,8 @@ STRUCTURE(laCanvasExtra){
     real ClickedX;
     real ClickedY;
 
+    int LW,ShowLegend;
+
     int HeightCoeff;
     //int        SnapBottom;
 
@@ -1041,6 +1042,7 @@ STRUCTURE(laWidget){
 #define LA_UI_FLAGS_NO_TOOLTIP LA_UI_FLAGS_PREFER_BOTTOM
 #define LA_UI_IMAGE_FULL_W     LA_TEXT_MONO
 #define LA_UI_MIN_WIDTH        LA_UI_FLAGS_COLOR_SPACE_CLAY
+#define LA_UI_FLAGS_NO_LABEL   (1<<29)
 
 #define LA_UI_FLAGS_INT_ICON  (LA_UI_FLAGS_NO_DECAL|LA_UI_FLAGS_NO_EVENT|LA_UI_FLAGS_ICON)
 #define LA_UI_FLAGS_PLAIN     (LA_UI_FLAGS_NO_DECAL|LA_UI_FLAGS_NO_EVENT)

+ 41 - 21
la_kernel.c

@@ -846,6 +846,8 @@ int laGetReadyWith(int GLMajor, int GLMinor, int BufferSamples){
 
     MAIN.InputMapping=memAcquire(sizeof(laRackPageCollection));
     MAIN.Drivers=memAcquire(sizeof(laRackPageCollection));
+    
+    MAIN.Animation=memAcquire(sizeof(laAnimation));
 
     //interactions:
     MAIN.TopFramerate = 60;
@@ -1434,11 +1436,11 @@ int la_SetUpUiListMatrix(laUiListDraw *uild, laUiList *Target, int _L, int _R, i
         return 0;
     }
 
-    tnsViewportWithScissor(uildi->L-1, PanelH - uildi->B-1, uildi->R - uildi->L+2, uildi->B - uildi->U+2);
-    tnsOrtho(Target->L + Target->PanX + uildi->DifX -1,
-             Target->L + Target->PanX + uildi->DifX +1+ (uildi->R - uildi->L),
-             Target->U + Target->PanY + uildi->DifY +1+ (uildi->B - uildi->U),
-             Target->U + Target->PanY + uildi->DifY -1,
+    tnsViewportWithScissor(uildi->L, PanelH - uildi->B, uildi->R - uildi->L, uildi->B - uildi->U-1);
+    tnsOrtho(Target->L + Target->PanX + uildi->DifX,
+             Target->L + Target->PanX + uildi->DifX+ (uildi->R - uildi->L),
+             Target->U + Target->PanY + uildi->DifY+ (uildi->B - uildi->U),
+             Target->U + Target->PanY + uildi->DifY+1,
              -100, 100);
 
     lstPushItem(&uild->Items, uildi);
@@ -2171,11 +2173,11 @@ void la_WindowDefDraw(laWindow *w, laBoxedTheme *bt){
     laUiItem* ui;
     if((ui=w->MaximizedUi) && w->MaximizedUiPanel && ui->Type->Draw && ui->CanvasTemplate->SecondDraw){
         MAIN.CurrentPanel=w->MaximizedUiPanel; laPanel* p=w->MaximizedUiPanel; int DrawState_=0;
+        la_SetPropMathcerContext(p);
         if(p->Refresh){
             tnsUseShader(T->immShader);tnsEnableShaderv(T->immShader);
             tnsUniformOutputColorSpace(T->immShader,w->OutputColorSpace);
             if(p->Refresh&LA_TAG_RECALC){
-                la_SetPropMathcerContext(p);
                 laRecalcPanelImmediate(p);
             }
             ui->Type->Draw(ui, LA_RH); tnsFlush();
@@ -3631,7 +3633,10 @@ laUiItem *la_UpdatePropDisplay(laUiItem *ui, laPropPack *Base, const char *Path,
             ui->Type = la_GetUiButtonType();
             if (OverrideType && (OverrideType->ForType == LA_PROP_OPERATOR)) ui->Type = OverrideType;
         }else{
-            ui->Type = (OverrideType && ((OverrideType->ForType == ui->PP.LastPs->p->PropertyType) || (OverrideType->TargetSub && !strcmp(OverrideType->TargetSub, ui->PP.LastPs->p->Identifier)))) ? OverrideType : la_GetUiTypeFromProperty(ui->PP.LastPs->p);
+            ui->Type = (OverrideType && ((!OverrideType->ForType)||
+                                        (OverrideType->ForType&&OverrideType->ForType == ui->PP.LastPs->p->PropertyType)||
+                                        (OverrideType->TargetSub && !strcmp(OverrideType->TargetSub, ui->PP.LastPs->p->Identifier)))) ?
+                                            OverrideType : la_GetUiTypeFromProperty(ui->PP.LastPs->p);
             ui->Flags|=ui->PP.LastPs->p->DefaultFlags;
         }
 
@@ -3640,7 +3645,10 @@ laUiItem *la_UpdatePropDisplay(laUiItem *ui, laPropPack *Base, const char *Path,
         }
     }else if (Base){
         ui->PP.LastPs = Base->LastPs;
-        ui->Type = (OverrideType && ((OverrideType->ForType == ui->PP.LastPs->p->PropertyType) || (OverrideType->TargetSub && !strcmp(OverrideType->TargetSub, ui->PP.LastPs->p->SubProp->Identifier)))) ? OverrideType : la_GetUiTypeFromProperty(ui->PP.LastPs->p);
+        ui->Type = (OverrideType && ((!OverrideType->ForType)||
+                                    (OverrideType->ForType&&OverrideType->ForType == ui->PP.LastPs->p->PropertyType)||
+                                    (OverrideType->TargetSub && !strcmp(OverrideType->TargetSub, ui->PP.LastPs->p->SubProp->Identifier)))) ?
+                                        OverrideType : la_GetUiTypeFromProperty(ui->PP.LastPs->p);
         ui->PP.RawThis = Base; //HACK! Not Unified For Prop Access!!!<<<----------??????????
         ui->Flags|=ui->PP.LastPs->p->DefaultFlags;
     }
@@ -4666,12 +4674,14 @@ STRUCTURE(laRowInfo){
     int CountElements;
     int U,MaxB,L;
     laListHandle Elements;
+    int LastNoHeight;
 };
 STRUCTURE(laRowNode){
     laListItem Item;
     laUiItem* ui;
     int GotW, LP, RP, H;
     int Expand;
+    int UseLast;
 };
 int la_InitRowNode(laRowInfo* ri, laUiItem* ui, laBoxedTheme* bt){
     ri->MaxW = ui->TR-ui->TL;//row node does not use margin
@@ -4689,6 +4699,10 @@ void la_AddRowNode(laRowInfo* ri, laUiItem* ui, laBoxedTheme* bt, int H){
     rn->H=H;
     rn->Expand=ui->Expand;
     lstAppendItem(&ri->Elements, rn);
+    if(ri->LastNoHeight){
+        ri->LastNoHeight=0; rn->UseLast=1; return; }
+    if(ui->Flags&LA_UI_FLAGS_UNDERNEATH){
+        ri->LastNoHeight=1; };
 
     if(!ri->UnitMinW){ri->UnitMinW=LA_RH;}
     ri->TotalPadding += (bt->LP+bt->RP);
@@ -4702,7 +4716,7 @@ void la_AddRowNode(laRowInfo* ri, laUiItem* ui, laBoxedTheme* bt, int H){
 int la_ShrinkableRowElements(laRowInfo* ri){
     int count=0;
     for(laRowNode* rn=ri->Elements.pFirst;rn;rn=rn->Item.pNext){
-        if(rn->GotW<=ri->UnitMinW) continue;
+        if(rn->GotW<=ri->UnitMinW || rn->UseLast) continue;
         count++;
     }
     return count;
@@ -4727,6 +4741,11 @@ int la_CalculateRowExpand(laRowInfo* ri, laUiItem* ui_end, int WaitAnimation){
     for(rn=ri->Elements.pFirst;rn;rn=rn->Item.pNext){
         laUiItem* ui=rn->ui;
         int NodeAdd, Node=rn->GotW;
+        if(rn->UseLast){
+            laRowNode* prevrn=rn->Item.pPrev; laUiItem* prevui=prevrn->ui;
+            rn->ui->TL=prevui->TL;rn->ui->TR=prevui->TR;rn->ui->TU=prevui->TU;rn->ui->TB=prevui->TB;
+            continue;
+        }
         if(Available>=0){
             NodeAdd=ri->ExpandAccum?(PerNode*rn->Expand):PerNode;
             NodeAdd+=(i<Remaining?1:0);i++;
@@ -4748,7 +4767,7 @@ int la_CalculateRowExpand(laRowInfo* ri, laUiItem* ui_end, int WaitAnimation){
         }
         if(ui->Type==_LA_UI_NODE_SOCKET){ la_RecordSocketRuntimePosition(ui); }
     }
-    ui_end->TB = ri->MaxB;
+    ui_end->TB = ui_end->Flags&LA_UI_FLAGS_UNDERNEATH?ui_end->TU:ri->MaxB;
     while(rn=lstPopItem(&ri->Elements)){
         FreeMem(rn);
     }
@@ -5057,11 +5076,12 @@ int la_UpdateUiListRecursive(laUiList *uil, int U, int L, int R, int B, int Fast
                 laUiTemplate *Template = ui->Template ? ui->Template : laGetPropertyUiDefine(&ui->PP, TInstance);
                 if(!Template) Template=laui_SubPropInfoDefault;
                 //ui->Template = Template;
+                int EraseWidth=(ui->Type == _LA_UI_COLLECTION_SELECTOR)?LA_RH:0;
                 if (!ui->Subs.pFirst && TInstance){
                     la_AddInstancePage(ui, TInstance, 0);
                     la_CalcUiTopInfluence(&uil->Columns, ui);
                     laMakeUiListFromTemplate(ui->Page, Template, &ParentPanel->PP, &ParentPanel->PropLinkPP, &ui->PP, 0, &uil->Columns, ui->TemplateContext);
-                    SubB = la_UpdateUiListRecursive(ui->Page, ui->TB+(NoDecal?0:bt->TM), ui->TL+(NoDecal?0:bt->LM), ui->TR-(NoDecal?0:bt->RM), B, Fast, ParentPanel);
+                    SubB = la_UpdateUiListRecursive(ui->Page, ui->TB+(NoDecal?0:bt->TM), ui->TL+(NoDecal?0:bt->LM), ui->TR-(NoDecal?0:bt->RM)-EraseWidth, B, Fast, ParentPanel);
                     ui->TB = SubB + (NoDecal?0:bt->TM);
                 }else if (ui->Subs.pFirst){
                     if (!TInstance || TInstance != ui->Page->Instance){
@@ -5071,12 +5091,12 @@ int la_UpdateUiListRecursive(laUiList *uil, int U, int L, int R, int B, int Fast
                             la_AddInstancePage(ui, TInstance, 0);
                             la_CalcUiTopInfluence(&uil->Columns, ui);
                             laMakeUiListFromTemplate(ui->Page, Template, &ParentPanel->PP, &ParentPanel->PropLinkPP, &ui->PP, 0, &uil->Columns, ui->TemplateContext);
-                            SubB = la_UpdateUiListRecursive(ui->Page, ui->TB+(NoDecal?0:bt->TM), ui->TL+(NoDecal?0:bt->LM), ui->TR-(NoDecal?0:bt->RM), B, Fast, ParentPanel);
+                            SubB = la_UpdateUiListRecursive(ui->Page, ui->TB+(NoDecal?0:bt->TM), ui->TL+(NoDecal?0:bt->LM), ui->TR-(NoDecal?0:bt->RM)-EraseWidth, B, Fast, ParentPanel);
                             ui->TB = SubB + (NoDecal?0:bt->TM);
                         }else
                             ui->TB = ui->TU + LA_RH + bt->BM;
                     }else{
-                        SubB = la_UpdateUiListRecursive(ui->Page, ui->TB+(NoDecal?0:bt->TM), ui->TL+(NoDecal?0:bt->LM), ui->TR-(NoDecal?0:bt->RM), B, Fast, ParentPanel);
+                        SubB = la_UpdateUiListRecursive(ui->Page, ui->TB+(NoDecal?0:bt->TM), ui->TL+(NoDecal?0:bt->LM), ui->TR-(NoDecal?0:bt->RM)-EraseWidth, B, Fast, ParentPanel);
                         ui->TB = SubB + (NoDecal?0:bt->TM);
                     }
                 }
@@ -5374,11 +5394,11 @@ void la_DrawUiListScrollerH(laUiList *uil, int DisplayOffset, int TotalW, int Di
     tnsDrawStringAuto("↔",laThemeColor(bt, LA_BT_BORDER),L-100,R+100,HU,LA_TEXT_REVERT_Y|LA_TEXT_ALIGN_CENTER);
     tnsFlush();
 }
-void la_DrawInstanceBkg(laUiList *uil, real* color){
+void la_DrawInstanceBkg(laUiList *uil, real* color, int LP, int RP){
     tnsUseNoTexture();
     tnsColor4dv(color);
-    tnsVertex2d(uil->L, uil->U); tnsVertex2d(uil->R, uil->U);
-    tnsVertex2d(uil->R, uil->B); tnsVertex2d(uil->L, uil->B);
+    tnsVertex2d(uil->L-LP, uil->U); tnsVertex2d(uil->R+RP, uil->U);
+    tnsVertex2d(uil->R+RP, uil->B); tnsVertex2d(uil->L-LP, uil->B);
     tnsPackAs(GL_TRIANGLE_FAN);
 }
 void la_InitSocketRecord(laUiListDraw* uild, laUiList* container){
@@ -5591,7 +5611,7 @@ int la_DrawUiListRecursive(laUiListDraw *uild, laUiList *uil, int L, int R, int
                 tnsFlush(); int DoNodes=RegisterNodes; int NoDecal=ui->Flags&LA_UI_FLAGS_NO_DECAL;
                 if(ui->Flags&LA_UI_FLAGS_NODE_CONTAINER){ la_InitSocketRecord(uild, ui->Page); DoNodes=1; }
                 Ret += la_DrawUiListRecursive(uild, ui->Page, ui->L+(NoDecal?0:bt->LM), ui->R-(NoDecal?0:bt->RM), U, B,
-                    (ui->Page->HeightCoeff ? ui->B - ui->Page->U : 10000), ConditionStackLevel, GlobalX, GlobalY, DoNodes);
+                    (ui->Page->HeightCoeff ? ui->B - ui->Page->U : 10000) -(NoDecal?0:bt->LM), ConditionStackLevel, GlobalX, GlobalY, DoNodes);
                 if (ui->Page->ScrollerShownH){ la_DrawUiListScrollerH(ui->Page, ui->Page->PanX,
                     ui->Page->R-ui->Page->L-bt->RM-bt->LM, ui->R-ui->Page->L-bt->RM-bt->LM-(ui->Page->ScrollerShownV?LA_SCROLL_W:0),ui->B); }
                 if (ui->Page->HeightCoeff) la_DrawUiListScrollerV(ui->Page, ui->Page->PanY,
@@ -5610,9 +5630,9 @@ int la_DrawUiListRecursive(laUiListDraw *uild, laUiList *uil, int L, int R, int
                     if(!(ui->Flags&LA_UI_COLLECTION_NO_HIGHLIGHT) && NeedDraw){
                         if (CanGetState){
                             State = laGetUiState(ui->PP.LastPs->p, sub->Instance);
-                            la_DrawInstanceBkg(sub, laAccentColor(LA_BT_NORMAL));
+                            la_DrawInstanceBkg(sub, laAccentColor(LA_BT_NORMAL),bt->LP,bt->RP);
                         }elif (sub->Instance == Active){
-                            la_DrawInstanceBkg(sub, laAccentColor(LA_BT_NORMAL));
+                            la_DrawInstanceBkg(sub, laAccentColor(LA_BT_NORMAL),bt->LP,bt->RP);
                         }
                     }
 
@@ -5627,7 +5647,7 @@ int la_DrawUiListRecursive(laUiListDraw *uild, laUiList *uil, int L, int R, int
                     if(CanGetTheme){
                         laTheme* t=laGetUiTheme(ui->PP.LastPs->p, ui->PP.LastPs->UseInstance, ui->PP.EndInstance);
                         la_SwitchThemeQuick(t, OriginalTheme);
-                        if(t) la_DrawInstanceBkg(sub, laThemeColor(_LA_THEME_FLOATING_PANEL ,LA_BT_NORMAL));
+                        if(t) la_DrawInstanceBkg(sub, laThemeColor(_LA_THEME_FLOATING_PANEL ,LA_BT_NORMAL),bt->LP,bt->RP);
                     }
 
                     tnsFlush();
@@ -5640,7 +5660,7 @@ int la_DrawUiListRecursive(laUiListDraw *uild, laUiList *uil, int L, int R, int
                 if(!(ui->Flags&LA_UI_FLAGS_NO_OVERLAY)){
                     for (sub = ui->Subs.pFirst; sub; sub = sub->Item.pNext){
                         tnsFlush();
-                        Ret += la_DrawUiListRecursive(uild, sub, L-1, R+1, U-1, B+1, 10000, ConditionStackLevel, GlobalX, GlobalY, RegisterNodes);
+                        Ret += la_DrawUiListRecursive(uild, sub, L-1, R+1, U-1, B+1, ui->B-bt->TP-bt->BP-bt->BM-bt->TM, ConditionStackLevel, GlobalX, GlobalY, RegisterNodes);
                     }
                 }
             }

+ 2 - 2
la_tns_kernel.c

@@ -3095,7 +3095,7 @@ void tnsDrawStringM(char *content, uint32_t* contentU, real Color[4], int L, int
     real sx = L; int sy = (LA_RH!=LA_RH0)?(((((real)FM->UsingFont->height/LA_RH0-0.5)/MAIN.UiScale)+0.5)*LA_RH + T):(FM->UsingFont->height+T);
     real MA=FM->UsingFont->MonoAdvance;
     int i,advance=1;
-    int len = contentU?strlenU(contentU):strlen(content);
+    int len = contentU?strlenU(contentU):(content?strlen(content):0); if(!len) return;
     real xo, yo, xl, yl;
     real TexCoord[12];
     real VertArr[12];
@@ -3186,7 +3186,7 @@ void tnsDrawStringWithPriority(char *Label, char *MajorContent, real Color[4], i
             tnsDrawStringM(MajorContent, 0, Color, L, R, T, Flags);
     }else{
         int LL = L, ML;
-        switch (TextAlign){
+        switch (Flags&LA_TEXT_ALIGN){
         case LA_TEXT_ALIGN_CENTER:
             ML = L + Str1W + (W - (Str1W + Str2W)) / 2;
             break;

+ 57 - 46
resources/la_modelling.c

@@ -400,6 +400,10 @@ int OPMOD_Select(laOperator *a, laEvent *e){
 #define LA_TRANSFORM_MODE_ROTATE 2
 #define LA_TRANSFORM_MODE_SCALE  3
 
+#define LA_TRANSFORM_RESTORE_LOC 1
+#define LA_TRANSFORM_RESTORE_ROT 2
+#define LA_TRANSFORM_RESTORE_SCA 3
+
 STRUCTURE(MTOrigObject){
     tnsObject* o;
     tnsMatrix44d Global;
@@ -640,7 +644,20 @@ void la_MakeTransformOperatorHint(laOperator* a, MTransformData* td){
     if(td->mode!=LA_TRANSFORM_MODE_SCALE){ strSafePrint(&a->RuntimeHint,"🆂 Scale  "); }
     if(td->mode!=LA_TRANSFORM_MODE_ROTATE){ strSafePrint(&a->RuntimeHint,"🆁 Rotate  "); }
 }
-int la_InitTransform(laOperator* a, laEvent* e, int mode){
+void la_RestoreTransform(tnsObject* ob, int restore_type, int restore_delta){
+    if(restore_delta){
+        if(restore_type==LA_TRANSFORM_RESTORE_LOC){ tnsVectorSet3(ob->DLocation,0,0,0); }
+        elif(restore_type==LA_TRANSFORM_RESTORE_ROT){ tnsVectorSet3(ob->DRotation,0,0,0); }
+        elif(restore_type==LA_TRANSFORM_RESTORE_SCA){ ob->DScale=1; }
+        tnsDeltaTransformValueChanged(ob);
+    }else{
+        if(restore_type==LA_TRANSFORM_RESTORE_LOC){ tnsVectorSet3(ob->Location,0,0,0); }
+        elif(restore_type==LA_TRANSFORM_RESTORE_ROT){ tnsVectorSet3(ob->Rotation,0,0,0); }
+        elif(restore_type==LA_TRANSFORM_RESTORE_SCA){ ob->Scale=1; }
+        tnsSelfTransformValueChanged(ob);
+    }
+}
+int la_InitTransform(laOperator* a, laEvent* e, int mode, int restore_type, int restore_delta){
     if(!a->This || !a->This->EndInstance){ return 0; }
     laCanvasExtra* ex=a->This->EndInstance; tnsCamera*c=ex->ViewingCamera; laUiItem* ui=ex->ParentUi;
     tnsObject*root=ui?ui->PP.EndInstance:0; if(!root) return 0;
@@ -656,6 +673,13 @@ int la_InitTransform(laOperator* a, laEvent* e, int mode){
         if(la_PopulateTransformVerticies(td, mo)){ ex->ClickedX=e->x; ex->ClickedY=e->y; ret=1; }
     }else{
         if(la_PopulateTransformObjects(td,root)){ ex->ClickedX=e->x; ex->ClickedY=e->y; ret=1; }
+        if(ret && restore_type){
+            for(int i=0;i<td->next;i++){ MTOrigObject* ob=arrElement(td->Originals, i, sizeof(MTOrigObject));
+                la_RestoreTransform(ob->o,restore_type,restore_delta);
+            }
+            la_RecordTransformDifferences(td); laNotifyUsers("tns.world"); la_FreeTransformData(td);
+            return 0;
+        }
     }
     
     if(ret){
@@ -669,13 +693,13 @@ int la_InitTransform(laOperator* a, laEvent* e, int mode){
     return 0;
 }
 int OPINV_Grab(laOperator *a, laEvent *e){
-    if(la_InitTransform(a, e, LA_TRANSFORM_MODE_GRAB)) return LA_RUNNING; return LA_FINISHED_PASS;
+    if(la_InitTransform(a, e, LA_TRANSFORM_MODE_GRAB,0,0)) return LA_RUNNING; return LA_FINISHED_PASS;
 }
 int OPINV_Scale(laOperator *a, laEvent *e){
-    if(la_InitTransform(a, e, LA_TRANSFORM_MODE_SCALE)) return LA_RUNNING; return LA_FINISHED_PASS;
+    if(la_InitTransform(a, e, LA_TRANSFORM_MODE_SCALE,0,0)) return LA_RUNNING; return LA_FINISHED_PASS;
 }
 int OPINV_Rotate(laOperator *a, laEvent *e){
-    if(la_InitTransform(a, e, LA_TRANSFORM_MODE_ROTATE)) return LA_RUNNING; return LA_FINISHED_PASS;
+    if(la_InitTransform(a, e, LA_TRANSFORM_MODE_ROTATE,0,0)) return LA_RUNNING; return LA_FINISHED_PASS;
 }
 int OPMOD_Transformation(laOperator *a, laEvent *e){
     if(!a->This || !a->This->EndInstance){ return 0; }
@@ -728,6 +752,30 @@ int OPMOD_Transformation(laOperator *a, laEvent *e){
 
     return LA_RUNNING;
 }
+int OPINV_ClearTransformation(laOperator *a, laEvent *e){
+    laCanvasExtra* ex=a->This->EndInstance; int restore=0;
+    char* channel=strGetArgumentString(a->ExtraInstructionsP,"channel");
+    if(channel){
+        if(channel[0]=='L' || channel[0]=='l'){ restore=LA_TRANSFORM_RESTORE_LOC; }
+        if(channel[0]=='R' || channel[0]=='r'){ restore=LA_TRANSFORM_RESTORE_ROT; }
+        if(channel[0]=='S' || channel[0]=='s'){ restore=LA_TRANSFORM_RESTORE_SCA; }
+    }else{
+        laEnableOperatorPanel(a,a->This,e->x,e->y,150,200,0,0,0,0,0,0,0,0,e); return LA_RUNNING;
+    }
+    char* dt=strGetArgumentString(a->ExtraInstructionsP,"delta");
+    int delta=dt?strSame(dt,"true"):ex->DeltaMode;
+    la_InitTransform(a,e,0,restore,delta);
+    return LA_FINISHED;
+}
+void laui_ClearTransformation(laUiList *uil, laPropPack *pp, laPropPack *actinst, laColumn *extracol, int context){
+    laColumn* c=laFirstColumn(uil), *cl,*cr; laSplitColumn(uil,c,0.6); cl=laLeftColumn(c,0); cr=laRightColumn(c,0);
+    laShowItemFull(uil,cl,pp,"_this_M_clear_transformations",0,"channel=loc;text=Location",0,0);
+    laShowItemFull(uil,cl,pp,"_this_M_clear_transformations",0,"channel=rot;text=Rotation",0,0);
+    laShowItemFull(uil,cl,pp,"_this_M_clear_transformations",0,"channel=sca;text=Scale",0,0);
+    laShowItemFull(uil,cr,pp,"_this_M_clear_transformations",0,"channel=loc;text=Delta;delta=true",0,0);
+    laShowItemFull(uil,cr,pp,"_this_M_clear_transformations",0,"channel=rot;text=Delta;delta=true",0,0);
+    laShowItemFull(uil,cr,pp,"_this_M_clear_transformations",0,"channel=sca;text=Delta;delta=true",0,0);
+}
 
 int la_ParentableRecursive(tnsObject* root, tnsObject* parent){
     for(laListItemPointer* lip=root->ChildObjects.pFirst;lip;lip=lip->pNext){
@@ -781,44 +829,6 @@ void laui_Unparent(laUiList *uil, laPropPack *pp, laPropPack *actinst, laColumn
     laShowItemFull(uil,c,pp,"_this_M_unparent",0,"action=unparent;keep_transform=false;text=Directly",0,0);
 }
 
-int la_ClearTransformationRecursive(tnsObject* root, int global,int location,int rotation,int scale){
-    int any=0; for(laListItemPointer* lip=root->ChildObjects.pFirst;lip;lip=lip->pNext){
-        tnsObject* o=lip->p; if(!(o->Flags&TNS_OBJECT_FLAGS_SELECTED)) continue;
-        if(location){
-            if(global){ o->GLocation[0]=o->GLocation[1]=o->GLocation[2]=0;}
-            else{ o->Location[0]=o->Location[1]=o->Location[2]=0; }
-        }
-        if(rotation){
-            if(global){ o->GRotation[0]=o->GRotation[1]=o->GRotation[2]=0;}
-            else{ o->Rotation[0]=o->Rotation[1]=o->Rotation[2]=0; }
-        }
-        if(scale){ if(global){ o->GScale=1;} else{ o->Scale=1; } }
-        if(global) tnsGlobalTransformValueChanged(o); else tnsSelfTransformValueChanged(o);
-        laRecordInstanceDifferences(o, "tns_object"); any++;
-        any+=la_ClearTransformationRecursive(o,global,location,rotation,scale);
-    } return any;
-}
-int OPINV_ClearTransformation(laOperator *a, laEvent *e){
-    if(!a->This || !a->This->EndInstance){ return 0; }
-    laCanvasExtra* ex=a->This->EndInstance; tnsCamera*c=ex->ViewingCamera; laUiItem* ui=ex->ParentUi;
-    tnsObject*root=ui?ui->PP.EndInstance:0; if(!root) return 0;
-
-    int global=0,location=0,rotation=0,scale=0;
-    if(strSame(strGetArgumentString(a->ExtraInstructionsP,"global"),"true")){ global=1; }
-    if(strSame(strGetArgumentString(a->ExtraInstructionsP,"location"),"true")){ location=1; }
-    if(strSame(strGetArgumentString(a->ExtraInstructionsP,"rotation"),"true")){ rotation=1; }
-    if(strSame(strGetArgumentString(a->ExtraInstructionsP,"scale"),"true")){ scale=1; }
-
-    int any=0;
-    if(location||rotation||scale){
-        any=la_ClearTransformationRecursive(root,global,location,rotation,scale);
-        if(any){ laPushDifferences("Clear Transformations", TNS_HINT_TRANSFORM); laNotifyUsers("tns.world"); }
-    }
-
-    return LA_FINISHED;
-}
-
-
 STRUCTURE(MEDupVert){
     int oi; tnsMVert* nmv; tnsMVert* omv; int IsBorder;
 };
@@ -935,14 +945,14 @@ int OPINV_Extrude(laOperator *a, laEvent *e){
     
     if(strSame(strGetArgumentString(a->ExtraInstructionsP,"duplicate_only"), "true")){
         la_FinishExtrude(ee, 1);
-        if(la_InitTransform(a, e, LA_TRANSFORM_MODE_GRAB)) return LA_RUNNING; return LA_FINISHED;
+        if(la_InitTransform(a, e, LA_TRANSFORM_MODE_GRAB,0,0)) return LA_RUNNING; return LA_FINISHED;
     }
 
     la_RemoveOriginalFaces(ee);
     la_ReconnectFaces(ee);
 
     la_FinishExtrude(ee, 1);
-    if(la_InitTransform(a, e, LA_TRANSFORM_MODE_GRAB)) return LA_RUNNING; return LA_FINISHED;
+    if(la_InitTransform(a, e, LA_TRANSFORM_MODE_GRAB,0,0)) return LA_RUNNING; return LA_FINISHED;
 
     return LA_FINISHED;
 }
@@ -1321,7 +1331,7 @@ int OPINV_Duplicate(laOperator *a, laEvent *e){
     
     if(ran){
         laRecordAndPush(0,"tns.world","Merge mesh objects",TNS_HINT_GEOMETRY); laNotifyUsers("tns.world");
-        if(la_InitTransform(a,e,LA_TRANSFORM_MODE_GRAB)) return LA_RUNNING; return LA_FINISHED;
+        if(la_InitTransform(a,e,LA_TRANSFORM_MODE_GRAB,0,0)) return LA_RUNNING; return LA_FINISHED;
     }
     
     return LA_FINISHED;
@@ -1588,11 +1598,12 @@ void la_RegisterModellingOperators(){
     laCreateOperatorType("M_grab", "Grab", "Grab things and move around", OPCHK_ViewportAndSceneExists, 0, 0, OPINV_Grab, OPMOD_Transformation, 0, LA_EXTRA_TO_PANEL);
     laCreateOperatorType("M_scale", "Scale", "Scale selected things", OPCHK_ViewportAndSceneExists, 0, 0, OPINV_Scale, OPMOD_Transformation, 0, LA_EXTRA_TO_PANEL);
     laCreateOperatorType("M_rotate", "Rotate", "Rotation selected things", OPCHK_ViewportAndSceneExists, 0, 0, OPINV_Rotate, OPMOD_Transformation, 0, LA_EXTRA_TO_PANEL);
+    at=laCreateOperatorType("M_clear_transformations", "Clear Transformations", "Clear object transformations", 0, 0, 0, OPINV_ClearTransformation, OPMOD_FinishOnData, 0, 0);
+    at->UiDefine=laui_ClearTransformation;
     at=laCreateOperatorType("M_make_parent", "Make Parent", "Parent objects to active objects or unparent selected ones", 0, 0, 0, OPINV_MakeParent, OPMOD_FinishOnData, 0, 0);
     at->UiDefine = laui_MakeParent;
     at=laCreateOperatorType("M_unparent", "Unparent", "Unparent selected objects", 0, 0, 0, OPINV_MakeParent, OPMOD_FinishOnData, 0, 0);
     at->UiDefine = laui_Unparent;
-    laCreateOperatorType("M_clear_transformations", "Clear Transformations", "Clear transformations in objects", 0, 0, 0, OPINV_ClearTransformation, 0, 0, 0);
     laCreateOperatorType("M_extrude", "Extrude", "Extrude parts of the mesh", 0, 0, 0, OPINV_Extrude, OPMOD_Transformation, 0, 0);
     at=laCreateOperatorType("M_delete", "Delete", "Delete parts of the mesh", 0, 0, 0, OPINV_Delete, OPMOD_FinishOnData, 0, 0);
     at->UiDefine=laui_Delete;

+ 39 - 8
resources/la_properties.c

@@ -514,6 +514,12 @@ void* laget_FirstDriverPage(void* unused, void* unused2){
 void* laget_CurrentRackPage(laRackPageCollection* c){
     return c->CurrentPage;
 }
+void* laget_CurrentAnimationAction(laAnimation* a){
+    return a->CurrentAction;
+}
+void* laset_CurrentAnimationAction(laAnimation* a, laAction* c){
+    memAssignRef(a,&a->CurrentAction,c);
+}
 
 void laset_WindowColorSpace(laWindow* w, int space){
     w->OutputColorSpace=space; if(w->win) laRedrawCurrentWindow();
@@ -826,6 +832,10 @@ laPropContainer* tnsget_ObjectType(tnsObject* o){
     }
 }
 
+int laget_AnimationActionCurrentFrame(laAction* aa){
+    return (aa->PlayHead*(real)aa->FrameCount+0.5);
+}
+
 void lareset_Main(void* Unused){
     return;
 }
@@ -1003,12 +1013,7 @@ void la_RegisterInternalProps(){
             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);
 
-            laAddSubGroup(p, "actions", "Actions", "All animation actions","la_animation_action",0,0,0,-1,0,0,0,0,0,0,offsetof(LA,Actions),0);
-            laAddFloatProperty(p, "animation_play_head","Play Head","Animation viewer global playhead",0,0,"s",0,0,0.1,0,0,offsetof(LA,AnimationPlayHead),0,0,0,0,0,0,0,0,0,0,0);
-            ep=laAddEnumProperty(p, "animation_play_status", "Play Status", "Animation viewer global play status", 0,0,0,0,0,offsetof(LA, AnimationPlayStatus),0,0,0,0,0,0,0,0,0,0);
-            laAddEnumItemAs(ep, "PAUSED", "Paused", "Animation is playing", LA_ANIMATION_STATUS_PAUSED, U'⏸');
-            laAddEnumItemAs(ep, "PLAY_FWD", "Playing", "File data is untouched", LA_ANIMATION_STATUS_PLAY_FWD,U'▶');
-            laAddEnumItemAs(ep, "PLAY_REV", "Reversing", "File data is untouched", LA_ANIMATION_STATUS_PLAY_REV,U'◀');
+            laAddSubGroup(p, "animation", "Animation", "Animation data","la_animation",0,0,0,offsetof(LA,Animation),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);
@@ -1407,12 +1412,38 @@ void la_RegisterInternalProps(){
             laAddStringProperty(p, "rename", "Rename", "Rename", 0,0,0,0,0,offsetof(laProp, Identifier), 0,0,0,laread_DetachedPropRename, 0);
         }
 
+
+        p = laAddPropertyContainer("la_animation", "Animation", "Animation data",0,0,sizeof(laAnimation),0,0,1);{
+            laAddSubGroup(p, "actions", "Actions", "All animation actions","la_animation_action",0,0,0,-1,0,laget_CurrentAnimationAction,0,laset_CurrentAnimationAction,0,0,offsetof(laAnimation,Actions),0);
+            laAddSubGroup(p, "current_action", "Current Action", "Current action","la_animation_action",0,0,0,offsetof(laAnimation,CurrentAction),0,0,0,0,0,0,0,LA_UDF_REFER);
+            
+            laAddFloatProperty(p, "play_head","Play Head","Animation viewer global playhead",0,0,"s",0,0,0.1,0,0,offsetof(laAnimation,PlayHead),0,0,0,0,0,0,0,0,0,0,0);
+            ep=laAddEnumProperty(p, "play_status", "Play Status", "Animation viewer global play status", 0,0,0,0,0,offsetof(laAnimation, PlayStatus),0,0,0,0,0,0,0,0,0,0);
+            laAddEnumItemAs(ep, "PAUSED", "Paused", "Animation is playing", LA_ANIMATION_STATUS_PAUSED, U'⏸');
+            laAddEnumItemAs(ep, "PLAY_FWD", "Playing", "File data is untouched", LA_ANIMATION_STATUS_PLAY_FWD,U'▶');
+            laAddEnumItemAs(ep, "PLAY_REV", "Reversing", "File data is untouched", LA_ANIMATION_STATUS_PLAY_REV,U'◀');
+        }
         p = laAddPropertyContainer("la_animation_action", "Action", "Animation action",0,0,sizeof(laAction),0,0,1);{
             laAddStringProperty(p,"name","Name","Name of the action",0,0,0,0,1,offsetof(laAction,Name),0,0,0,0,LA_AS_IDENTIFIER);
             laAddSubGroup(p, "channels", "Channels", "Action channels", "la_animation_channel",0,0,0,-1,0,0,0,0,0,0,offsetof(laAction, Channels),0);
-            laAddFloatProperty(p, "length","Length","Length of the action in seconds",0,0,"s",40,0,0.1,5,0,offsetof(laAction,Length),0,0,0,0,0,0,0,0,0,0,0);
-            laAddFloatProperty(p, "play_head","Play Head","PlayHead position",0,0,"s",30,0,0.1,5,0,offsetof(laAction,PlayHead),0,0,0,0,0,0,0,0,0,0,0);
+            laAddFloatProperty(p, "length","Length","Length of the action in seconds",0,0,"s",30,0,0.1,5,0,offsetof(laAction,Length),0,0,0,0,0,0,0,0,0,0,0);
+            laAddFloatProperty(p, "play_head","Play Head","Play Head position",0,0,0,1.0,0,0.01,0,0,offsetof(laAction,PlayHead),0,0,0,0,0,0,0,0,0,0,0);
+            laAddFloatProperty(p, "offset","Offset","Play head offset from global timing",0,0,0,1.0,0,0.01,0,0,offsetof(laAction,Offset),0,0,0,0,0,0,0,0,0,0,0);
             laAddIntProperty(p, "frame_count","Frame Count","Total frame count in the whole length of the action",0,0,0,0,0,0,120,0,offsetof(laAction,FrameCount),0,0,0,0,0,0,0,0,0,0,0);
+            laAddIntProperty(p, "current_frame","Current Frame","Current frame in this action",0,0,0,0,0,0,0,0,0,laget_AnimationActionCurrentFrame,0,0,0,0,0,0,0,0,0,LA_READ_ONLY);
+            ep = laAddEnumProperty(p, "mute", "Mute", "Mute this action", 0,0,0,0,0,offsetof(laAction, Mute),0,0,0,0,0,0,0,0,0,0);{
+                laAddEnumItemAs(ep, "NONE", "None", "Not muted", 0,U'M');
+                laAddEnumItemAs(ep, "MUTE", "Mute", "Muted", 1,U'🄼');
+            }
+            ep = laAddEnumProperty(p, "solo", "Solo", "Solo play this action", 0,0,0,0,0,offsetof(laAction, Solo),0,0,0,0,0,0,0,0,0,0);{
+                laAddEnumItemAs(ep, "NONE", "None", "Play this action with", 0,U'S');
+                laAddEnumItemAs(ep, "SOLO", "Solo", "Solo play", 1,U'🅂');
+            }
+            ep = laAddEnumProperty(p, "play_mode", "Play Mode", "How to play this action", 0,0,0,0,0,offsetof(laAction, PlayMode),0,0,0,0,0,0,0,0,0,0);{
+                laAddEnumItemAs(ep, "REPEAT", "Repeat", "Play action in repeat", LA_ANIMATION_PLAY_MODE_REPEAT, U'⮆');
+                laAddEnumItemAs(ep, "HOLD", "Hold", "Hold end values when time is outside time range", LA_ANIMATION_PLAY_MODE_HOLD,U'⭲');
+                laAddEnumItemAs(ep, "BOUNCE", "Bounce", "Play action back and forth", LA_ANIMATION_PLAY_MODE_BOUNCE,U'⮀');
+            }
         }
         p = laAddPropertyContainer("la_animation_channel", "Channel", "Animation channel",0,0,sizeof(laAnimationChannel),0,0,1);{
             laAddSubGroup(p, "keys", "Keys", "Key Frames", "la_animation_key",0,0,0,-1,0,0,0,0,0,0,offsetof(laAnimationChannel, Keys),0);

+ 96 - 14
resources/la_templates.c

@@ -1564,33 +1564,115 @@ void laui_Drivers(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *
     laShowItemFull(uil,c,Extra,"page",LA_WIDGET_COLLECTION_SINGLE,0,laui_RackPage,0)->Flags|=LA_UI_FLAGS_NO_DECAL;;
 }
 
+void laui_AnimationActionSimple(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *ExtraColumns, int context){
+    laColumn* c=laFirstColumn(uil);
+    laUiItem* b=laBeginRow(uil,c,0,0);{
+        laUiItem* b2=laOnConditionThat(uil,c,laEqual(laPropExpression(This,"__self"),laPropExpression(0,"la.animation.actions")));{
+            laShowLabel(uil,c,"🠶",0,0)->Flags|=LA_TEXT_MONO|LA_TEXT_ALIGN_RIGHT;
+        }laElse(uil,b2);{
+            laShowLabel(uil,c,"  ",0,0)->Flags|=LA_TEXT_MONO;
+        }laEndCondition(uil,b2);
+        laUiItem* ui=laShowItemFull(uil,c,This,"play_head",LA_WIDGET_VALUE_METER,0,0,0);
+        ui->Flags|=LA_UI_FLAGS_NO_LABEL|LA_UI_FLAGS_UNDERNEATH; ui->Expand=1;
+        laShowItem(uil,c,This,"name")->Flags|=LA_UI_FLAGS_NO_DECAL|LA_UI_FLAGS_NO_EVENT;
+        laShowItemFull(uil,c,This,"solo",0,0,0,0)->Flags=LA_UI_FLAGS_ICON|LA_UI_FLAGS_CYCLE|LA_UI_FLAGS_HIGHLIGHT;
+        laShowItemFull(uil,c,This,"mute",0,0,0,0)->Flags=LA_UI_FLAGS_ICON|LA_UI_FLAGS_CYCLE|LA_UI_FLAGS_HIGHLIGHT;
+    }
+    laEndRow(uil,b);
+}
+void laui_AnimationActionListItem(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *ExtraColumns, int context){
+    laColumn* c=laFirstColumn(uil); laUiItem* ui;
+    laUiItem* b=laBeginRow(uil,c,0,0);{
+        //laUiItem* b2=laOnConditionThat(uil,c,laNot(laEqual(laPropExpression(This,"__self"),laPropExpression(0,"la.animation.actions"))));{
+        //    ui=laShowItemFull(uil,c,This,"play_head",LA_WIDGET_VALUE_METER,0,0,0);
+        //    ui->Flags|=LA_UI_FLAGS_NO_LABEL|LA_UI_FLAGS_UNDERNEATH; ui->Expand=1;
+        //}laEndCondition(uil,b2);
+        ui=laShowItem(uil,c,This,"name");
+        ui->Flags|=LA_UI_FLAGS_NO_DECAL|LA_UI_FLAGS_NO_EVENT; ui->Expand=1;
+    }
+    laEndRow(uil,b);
+}
 void laui_AnimationActions(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
-    laColumn* c=laFirstColumn(uil),*cl, *cr; laSplitColumn(uil,c,0.5); cl=laLeftColumn(c,5); cr=laRightColumn(c,0);
+    laColumn* c=laFirstColumn(uil),*cl, *cr; laSplitColumn(uil,c,0.5); cl=laLeftColumn(c,0); cr=laRightColumn(c,5);
     laUiItem* b,*ui;
 
-    laUiItem* row=laBeginRow(uil,cl,0,0);
-    laShowItemFull(uil,c,0,"LA_animation_reset_time",0,0,0,0)->Flags|=LA_UI_FLAGS_ICON|LA_UI_FLAGS_EXIT_WHEN_TRIGGERED;
-    b=laOnConditionThat(uil,c,laPropExpression(0,"la.animation_play_status"));{
-        ui=laShowItemFull(uil,c,0,"LA_animation_set_play_status",0,"text=❚❚;",0,0);
-        ui->Flags|=LA_UI_FLAGS_EXIT_WHEN_TRIGGERED|LA_TEXT_ALIGN_CENTER; ui->Expand=10;
+    ui=laShowItemFull(uil,cl,0,"la.animation.play_head",LA_WIDGET_INT_PLAIN,0,0,0);
+    ui->Flags|=LA_UI_FLAGS_NO_LABEL; ui->Expand=1;
+
+    laUiItem* row=laBeginRow(uil,cr,0,0);
+    laShowItemFull(uil,cr,0,"LA_animation_reset_time",0,0,0,0)->Flags|=LA_UI_FLAGS_ICON|LA_UI_FLAGS_EXIT_WHEN_TRIGGERED;
+    b=laOnConditionThat(uil,cr,laPropExpression(0,"la.animation.play_status"));{
+        ui=laShowItemFull(uil,cr,0,"LA_animation_set_play_status",0,"text=❚❚;",0,0);
+        ui->Flags|=LA_UI_FLAGS_EXIT_WHEN_TRIGGERED|LA_TEXT_ALIGN_CENTER; ui->Expand=1;
     }laElse(uil,b);{
-        ui=laShowItemFull(uil,c,0,"LA_animation_set_play_status",0,"icon=◀;mode=reverse;",0,0);
-        ui->Flags|=LA_UI_FLAGS_ICON|LA_UI_FLAGS_EXIT_WHEN_TRIGGERED; ui->Expand=10;
-        ui=laShowItemFull(uil,c,0,"LA_animation_set_play_status",0,"icon=▶;mode=forward;",0,0);
-        ui->Flags|=LA_UI_FLAGS_ICON|LA_UI_FLAGS_EXIT_WHEN_TRIGGERED; ui->Expand=10;
+        ui=laShowItemFull(uil,cr,0,"LA_animation_set_play_status",0,"icon=◀;mode=reverse;",0,0);
+        ui->Flags|=LA_UI_FLAGS_ICON|LA_UI_FLAGS_EXIT_WHEN_TRIGGERED; ui->Expand=1;
+        ui=laShowItemFull(uil,cr,0,"LA_animation_set_play_status",0,"icon=▶;mode=forward;",0,0);
+        ui->Flags|=LA_UI_FLAGS_ICON|LA_UI_FLAGS_EXIT_WHEN_TRIGGERED; ui->Expand=1;
     }laEndCondition(uil,b);
     laEndRow(uil,row);
 
-    laShowItemFull(uil,cr,0,"la.animation_play_head",LA_WIDGET_INT_PLAIN,0,0,0);
-    
-    laShowItemFull(uil,c,0,"la.actions",LA_WIDGET_COLLECTION,0,0,0);
+    ui=laMakeEmptyGroup(uil,c,"Use Format",0); laUiList* gu=ui->Page; laColumn* gc=laFirstColumn(gu);
+    gu->HeightCoeff=-3; //ui->Flags|=LA_UI_FLAGS_NO_DECAL;
+    laShowItemFull(gu,gc,0,"la.animation.actions",LA_WIDGET_COLLECTION,0,laui_AnimationActionSimple,0)->Flags|=LA_UI_FLAGS_NO_DECAL;
+
+    row=laBeginRow(uil,cl,0,0);
+    laShowItemFull(uil,cl,0,"LA_animation_new_action",0,"text=New",0,0);//->Flags|=LA_UI_FLAGS_ICON;
+    laEndRow(uil,row);
+}
+void laui_AnimationActionChannels(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil),*cl, *cr, *cll,*clr, *crl, *crr, *crrl,*crrr;
+    laSplitColumn(uil,c,0.3); cl=laLeftColumn(c,6); cr=laRightColumn(c,0);
+    laSplitColumn(uil,cl,0.75); cll=laLeftColumn(cl,0); clr=laRightColumn(cl,1);
+    laSplitColumn(uil,cr,0.6); crl=laLeftColumn(cr,0); crr=laRightColumn(cr,12);
+    laSplitColumn(uil,crr,0.5); crrl=laLeftColumn(crr,0); crrr=laRightColumn(crr,0);
+    laUiItem* b,*ui,*b2;
+
+    laUiItem* row=laBeginRow(uil,cll,0,0);
+    laShowItemFull(uil,cll,0,"LA_animation_reset_time",0,0,0,0)->Flags|=LA_UI_FLAGS_ICON|LA_UI_FLAGS_EXIT_WHEN_TRIGGERED;
+    b=laOnConditionThat(uil,cll,laPropExpression(0,"la.animation.play_status"));{
+        ui=laShowItemFull(uil,cll,0,"LA_animation_set_play_status",0,"text=❚❚;",0,0);
+        ui->Flags|=LA_UI_FLAGS_EXIT_WHEN_TRIGGERED|LA_TEXT_ALIGN_CENTER; ui->Expand=1;
+    }laElse(uil,b);{
+        ui=laShowItemFull(uil,cll,0,"LA_animation_set_play_status",0,"icon=◀;mode=reverse;",0,0);
+        ui->Flags|=LA_UI_FLAGS_ICON|LA_UI_FLAGS_EXIT_WHEN_TRIGGERED; ui->Expand=1;
+        ui=laShowItemFull(uil,cll,0,"LA_animation_set_play_status",0,"icon=▶;mode=forward;",0,0);
+        ui->Flags|=LA_UI_FLAGS_ICON|LA_UI_FLAGS_EXIT_WHEN_TRIGGERED; ui->Expand=1;
+    }laEndCondition(uil,b);
+    laEndRow(uil,row);
+
+    b2=laOnConditionThat(uil,c,laPropExpression(0,"la.animation.current_action"));{
+        row=laBeginRow(uil,crl,0,0);
+        ui=laShowItemFull(uil,crl,0,"la.animation.current_action.play_head",LA_WIDGET_VALUE_METER,0,0,0);
+        ui->Flags|=LA_UI_FLAGS_NO_LABEL|LA_UI_FLAGS_UNDERNEATH; ui->Expand=1;
+        laShowItem(uil,crl,0,"la.animation.current_action.current_frame")->Flags|=LA_UI_FLAGS_NO_LABEL|LA_UI_FLAGS_NO_DECAL;
+        laShowSeparator(uil,crl);
+        laShowItem(uil,crl,0,"la.animation.current_action.frame_count")->Flags|=LA_TEXT_ALIGN_CENTER;
+        laShowItem(uil,crl,0,"la.animation.current_action.length");
+        laEndRow(uil,row);
+        laShowItem(uil,crrl,0,"la.animation.current_action.name");
+        laShowItemFull(uil,crrr,0,"la.animation.actions",LA_WIDGET_COLLECTION_SELECTOR,0,laui_AnimationActionListItem,0);
+        b=laOnConditionToggle(uil,clr,0,0,0,0,0);{
+            row=laBeginRow(uil,cr,0,0);
+            laShowItem(uil,cr,0,"la.animation.current_action.play_mode")->Flags|=LA_UI_FLAGS_EXPAND;
+            laShowSeparator(uil,cr);
+            laShowItem(uil,cr,0,"la.animation.current_action.solo")->Flags|=LA_UI_FLAGS_CYCLE|LA_UI_FLAGS_HIGHLIGHT;
+            laShowItem(uil,cr,0,"la.animation.current_action.mute")->Flags|=LA_UI_FLAGS_CYCLE|LA_UI_FLAGS_HIGHLIGHT;
+            laEndRow(uil,row);
+        }laEndCondition(uil,b);
+    }laElse(uil,b2);{
+        laShowLabel(uil,crl,"Please select an action.",0,0);
+        laShowItemFull(uil,crrr,0,"la.animation.actions",LA_WIDGET_COLLECTION_SELECTOR,0,laui_AnimationActionListItem,0);
+    }laEndCondition(uil,b2);
+
 
-    laShowItem(uil,c,0,"LA_animation_new_action");
+    laShowCanvas(uil,c,0,"la.animation.current_action","la_AnimationActionDrawCanvas",-1);
 }
 
 
 void la_RegisterBuiltinTemplates(){
     laRegisterUiTemplate("LAUI_animation_actions", "Actions", laui_AnimationActions, 0, 0, 0, 0,15,25);
+    laRegisterUiTemplate("LAUI_animation_action_channels", "Action Channels", laui_AnimationActionChannels, 0, 0, 0, 0,20,15);
     laRegisterUiTemplate("LAUI_input_mapper","Input Mapper",laui_InputMapper,0,0,"Controlling",0,0,0);
     laRegisterUiTemplate("LAUI_drivers","Drivers",laui_Drivers,lauidetached_Drivers,0,0,0,0,0);
     laRegisterUiTemplate("LAUI_controllers", "Controllers", laui_GameController, lauidetached_GameController, 0,0,0,0,0);

+ 18 - 9
resources/la_widgets.c

@@ -204,7 +204,9 @@ int la_ColorSelectorGetMinWidth(laUiItem *ui){
 }
 int la_ValueGetMinWidth(laUiItem *ui){
     int ExtraW=0;
-    if(ui->Flags&LA_UI_FLAGS_EXPAND){ ExtraW+=tnsStringGetWidth(transLate(ui->PP.LastPs->p->Name),0,0);  }
+    if((ui->Flags&LA_UI_FLAGS_EXPAND)&&(!(ui->Flags&LA_UI_FLAGS_NO_LABEL))){
+        ExtraW+=tnsStringGetWidth(transLate(ui->PP.LastPs->p->Name),0,0);
+    }
     return LA_RH*4+ExtraW;
 }
 int la_LabelGetMinWidth(laUiItem *ui){
@@ -246,9 +248,11 @@ int la_EnumGetMinWidth(laUiItem *ui){
                     tW = tnsStringGetWidth(transLate(buf), 0, ui->Flags&LA_TEXT_MONO) + SharedWidth;
                 }
             }else{
+                for (i = ep->Items.pFirst; i; i = i->Item.pNext){ if (i->IconID){ HasIcon=1; } }
                 tW = tnsStringGetWidth(transLate(ep->Base.Name), 0, ui->Flags&LA_TEXT_MONO) + SharedWidth;
             }
             if (tW > W) W = tW;
+            if(HasIcon) W+=bt->LM+LA_RH;
         }else{
             for (i = ep->Items.pFirst; i; i = i->Item.pNext){
                 tW = tnsStringGetWidth(transLate(i->Name), 0, ui->Flags&LA_TEXT_MONO) + SharedWidth;
@@ -562,6 +566,7 @@ void la_IntDraw(laUiItem *ui, int h){
     int IsVertical=ui->Flags&LA_UI_FLAGS_TRANSPOSE;
     int IsIcon=ui->Flags&LA_UI_FLAGS_ICON;
     int NoDecal=ui->Flags&LA_UI_FLAGS_NO_DECAL;
+    int NoLabel=ui->Flags&LA_UI_FLAGS_NO_LABEL;
 
     if (laIsPropertyReadOnly(&ui->PP) && !NoDecal) ui->State = LA_BT_DISABLED;
 
@@ -627,7 +632,7 @@ void la_IntDraw(laUiItem *ui, int h){
 
             tnsDrawStringM(0, buf, laThemeColor(bt, LA_BT_TEXT_ACTIVE), _L + bt->LM, _R - bt->RM, _U, ui->Flags);
         }else{
-            tnsDrawStringWithPriority(buf2, buf, laThemeColor(bt, LA_BT_TEXT|ui->State), bt->TextAlign, _L + bt->LM, _R - bt->RM, _U, ui->Flags);//, ui->ExtraInstructions);
+            tnsDrawStringWithPriority(NoLabel?0:buf2, buf, laThemeColor(bt, LA_BT_TEXT|ui->State), bt->TextAlign, _L + bt->LM, _R - bt->RM, _U, ui->Flags);//, ui->ExtraInstructions);
         }
         if (ui->Extra && ui->Extra->On == i + 1) ui->State = Original;
     }
@@ -648,6 +653,7 @@ void la_FloatDraw(laUiItem *ui, int h){
     int IsVertical=ui->Flags&LA_UI_FLAGS_TRANSPOSE;
     int NoDecal=ui->Flags&LA_UI_FLAGS_NO_DECAL;
     int IsRad=ui->PP.LastPs->p->IsRadAngle;
+    int NoLabel=ui->Flags&LA_UI_FLAGS_NO_LABEL;
 
     if (laIsPropertyReadOnly(&ui->PP) && !NoDecal) ui->State = LA_BT_DISABLED;
 
@@ -709,7 +715,7 @@ void la_FloatDraw(laUiItem *ui, int h){
 
             tnsDrawStringM(0, buf, laThemeColor(bt, LA_BT_TEXT_ACTIVE), _L + bt->LM, _R - bt->RM, _U, ui->Flags);
         }else{
-            tnsDrawStringWithPriority(buf2, buf, laThemeColor(bt, LA_BT_TEXT|ui->State), bt->TextAlign, _L + bt->LM, _R - bt->RM, _U, ui->Flags);//, ui->ExtraInstructions);
+            tnsDrawStringWithPriority(NoLabel?0:buf2, buf, laThemeColor(bt, LA_BT_TEXT|ui->State), bt->TextAlign, _L + bt->LM, _R - bt->RM, _U, ui->Flags);//, ui->ExtraInstructions);
         }
         if (ui->Extra && ui->Extra->On == i + 1) ui->State = Original;
     }
@@ -1406,6 +1412,7 @@ void la_ValueMeterDraw(laUiItem *ui, int h){
     int Imin=-100, Imax=100;
     real min=-100, max=100;
     int s, State; int IsVertical=ui->Flags&LA_UI_FLAGS_TRANSPOSE;
+    int NoLabel=ui->Flags&LA_UI_FLAGS_NO_LABEL;
 
     int IsInt=(ui->PP.LastPs->p->PropertyType&LA_PROP_INT)?1:0;
     Len = laGetArrayLength(&ui->PP);
@@ -1448,9 +1455,10 @@ void la_ValueMeterDraw(laUiItem *ui, int h){
         tnsVertex2d(_R, _B); tnsVertex2d(_L, _B);
         tnsPackAs(GL_LINE_LOOP);
 
-        if(Len==1){ sprintf(buf,ui->PP.LastPs->p->Name); } else{ if(i<8)sprintf(buf,prefix[i]); }
-
-        tnsDrawStringAuto(buf, laThemeColor(bt, LA_BT_TEXT), _L + bt->LM, _R - bt->RM, _U, ui->Flags);//, ui->ExtraInstructions);
+        if(!NoLabel){
+            if(Len==1){ sprintf(buf,ui->PP.LastPs->p->Name); } else{ if(i<8)sprintf(buf,prefix[i]); }
+            if(buf[0]) tnsDrawStringAuto(buf, laThemeColor(bt, LA_BT_TEXT), _L + bt->LM, _R - bt->RM, _U, ui->Flags);//, ui->ExtraInstructions);
+        }
     }
 
     tnsFlush();
@@ -1561,10 +1569,11 @@ void la_RegisterUiTypesBasic(){
     _LA_UI_INT->GetMinWidth=la_ValueGetMinWidth;
 
     LA_WIDGET_VALUE_METER->Type=
-    _LA_UI_VALUE_METER = la_RegisterUiType("LA_value_meter", LA_PROP_INT | LA_PROP_ARRAY, 0, &_LA_THEME_VALUATOR, la_ValueMeterDraw, la_ValueMeterGetHeight, la_GeneralUiInit, la_GeneralUiDestroy);
-
+    _LA_UI_VALUE_METER = la_RegisterUiType("LA_value_meter", 0, 0, &_LA_THEME_VALUATOR, la_ValueMeterDraw, la_ValueMeterGetHeight, la_GeneralUiInit, la_GeneralUiDestroy);
+    _LA_UI_VALUE_METER->GetMinWidth=la_ValueGetMinWidth;
+    
     LA_WIDGET_VALUE_METER_2D->Type=
-    _LA_UI_VALUE_METER_2D = la_RegisterUiType("LA_value_meter_2d", LA_PROP_INT | LA_PROP_ARRAY, 0, &_LA_THEME_VALUATOR, la_ValueMeter2DDraw, la_ValueMeter2DGetHeight, la_GeneralUiInit, la_GeneralUiDestroy);
+    _LA_UI_VALUE_METER_2D = la_RegisterUiType("LA_value_meter_2d", 0, 0, &_LA_THEME_VALUATOR, la_ValueMeter2DDraw, la_ValueMeter2DGetHeight, la_GeneralUiInit, la_GeneralUiDestroy);
 
     LA_WIDGET_FLOAT->Type=LA_WIDGET_FLOAT_PLAIN->Type=
     _LA_UI_FLOAT = la_RegisterUiType("LA_real_array_horizon", LA_PROP_FLOAT | LA_PROP_ARRAY, "LA_real_array_h_operator",

+ 5 - 4
resources/la_widgets_viewers.c

@@ -1070,7 +1070,6 @@ void la_RegisterUiTypesViewerWidgets(){
         laAddOperatorProperty(pc, "_this_M_grab", "Grab", "Grab things and move around", "M_grab", 0, 0);
         laAddOperatorProperty(pc, "_this_M_scale", "Scale", "Scale selected things", "M_scale", 0, 0);
         laAddOperatorProperty(pc, "_this_M_rotate", "Rotate", "Rotation selected things", "M_rotate", 0, 0);
-        laAddOperatorProperty(pc, "_this_M_clear_transformations", "Clear Transformations", "Clear object transformation values", "M_clear_transformations", 0, 0);
         laAddOperatorProperty(pc, "_this_M_extrude", "Extrude", "Extrude parts of the mesh", "M_extrude", 0, 0);
         laAddOperatorProperty(pc, "_this_M_delete", "Delete", "Delete parts of the mesh", "M_delete", 0, 0);
         laAddOperatorProperty(pc, "_this_M_make", "Make", "Make mesh primitive from selected ones", "M_make", 0, 0);
@@ -1082,6 +1081,7 @@ void la_RegisterUiTypesViewerWidgets(){
         laAddOperatorProperty(pc, "_this_M_recalculate_normals", "Recalculate Normals", "Recalculate normals", "M_recalculate_normals", 0, 0);
         laAddOperatorProperty(pc, "_this_M_knife", "Knife", "Cut though edges", "M_knife", 0, 0);
         laAddOperatorProperty(pc, "_this_M_merge", "Merge", "Merge vertices", "M_merge", 0, 0);
+        laAddOperatorProperty(pc, "_this_M_clear_transformations", "Clear Transformations", "Clear object transformations", "M_clear_transformations", 0, 0);
     }
 
     km = &ct->KeyMapper;
@@ -1107,11 +1107,12 @@ void la_RegisterUiTypesViewerWidgets(){
     laAssignNewKey(km, 0, "M_grab", LA_KM_SEL_UI_EXTRA, 0, LA_KEY_DOWN, 'g', 0);
     laAssignNewKey(km, 0, "M_scale", LA_KM_SEL_UI_EXTRA, 0, LA_KEY_DOWN, 's', 0);
     laAssignNewKey(km, 0, "M_rotate", LA_KM_SEL_UI_EXTRA, 0, LA_KEY_DOWN, 'r', 0);
+    laAssignNewKey(km, 0, "M_clear_transformations", LA_KM_SEL_UI_EXTRA, LA_KEY_ALT, LA_KEY_DOWN, 'g', "channel=loc");
+    laAssignNewKey(km, 0, "M_clear_transformations", LA_KM_SEL_UI_EXTRA, LA_KEY_ALT, LA_KEY_DOWN, 's', "channel=sca");
+    laAssignNewKey(km, 0, "M_clear_transformations", LA_KM_SEL_UI_EXTRA, LA_KEY_ALT, LA_KEY_DOWN, 'r', "channel=rot");
+    laAssignNewKey(km, 0, "M_clear_transformations", LA_KM_SEL_UI_EXTRA, LA_KEY_ALT, LA_KEY_DOWN, 't', 0);
     laAssignNewKey(km, 0, "M_make_parent", LA_KM_SEL_UI_EXTRA, LA_KEY_CTRL, LA_KEY_DOWN, 'p', 0);
     laAssignNewKey(km, 0, "M_unparent", LA_KM_SEL_UI_EXTRA, LA_KEY_ALT, LA_KEY_DOWN, 'p', 0);
-    laAssignNewKey(km, 0, "M_clear_transformations", LA_KM_SEL_UI_EXTRA, LA_KEY_ALT, LA_KEY_DOWN, 'g', "location=true;text=Clear Location;");
-    laAssignNewKey(km, 0, "M_clear_transformations", LA_KM_SEL_UI_EXTRA, LA_KEY_ALT, LA_KEY_DOWN, 'r', "rotation=true;text=Clear Rotation;");
-    laAssignNewKey(km, 0, "M_clear_transformations", LA_KM_SEL_UI_EXTRA, LA_KEY_ALT, LA_KEY_DOWN, 's', "scale=true;text=Clear Scale;");
     laAssignNewKey(km, 0, "M_extrude", LA_KM_SEL_UI_EXTRA, 0, LA_KEY_DOWN, 'e', 0);
     laAssignNewKey(km, 0, "M_extrude", LA_KM_SEL_UI_EXTRA, LA_KEY_SHIFT, LA_KEY_DOWN, 'd', "duplicate_only=true;text=Duplicate;");
     laAssignNewKey(km, 0, "M_delete", LA_KM_SEL_UI_EXTRA, 0, LA_KEY_DOWN, 'x', 0);