*/}}
Quellcode durchsuchen

Application customization APIs.

Yiming Wu vor 2 Jahren
Ursprung
Commit
8f5ecfa00d
14 geänderte Dateien mit 288 neuen und 177 gelöschten Zeilen
  1. 1 0
      .gitignore
  2. 4 1
      la_controllers.c
  3. 16 9
      la_data.c
  4. 5 0
      la_data.h
  5. 39 9
      la_interface.h
  6. 51 13
      la_kernel.c
  7. 2 2
      la_tns.h
  8. 41 51
      la_tns_kernel.c
  9. 3 3
      la_util.c
  10. 1 1
      la_util.h
  11. 45 15
      resources/la_operators.c
  12. 3 0
      resources/la_properties.c
  13. 75 70
      resources/la_templates.c
  14. 2 3
      resources/la_widgets.c

+ 1 - 0
.gitignore

@@ -2,3 +2,4 @@ build/*
 .vscode/
 screenshots/
 *.udf
+fonts/

+ 4 - 1
la_controllers.c

@@ -9,6 +9,7 @@
 #include <linux/input.h>
 #include <linux/joystick.h>
 #include <errno.h>
+#include <X11/extensions/XInput2.h>
 
 extern LA MAIN;
 
@@ -99,7 +100,9 @@ void la_RefreshControllers(){
     la_InitControllers();
 }
 int OPINV_RefreshControllers(){
-    la_RefreshControllers(); return LA_FINISHED;
+    la_RefreshControllers();
+    la_ScanWacomDevices(MAIN.dpy,XIAllDevices);
+    return LA_FINISHED;
 }
 
 

+ 16 - 9
la_data.c

@@ -234,7 +234,7 @@ void *la_FindMatchingInstance(void *From, laProp *Sub, laProp *p, laPropStep *Va
 int la_GetPropFromPath(laPropPack *Self, laPropPack *Base, const char *Path, void **SpecifiedInstance){
     laProp *p = 0;
     laListHandle *lst=0, *lstB = 0;
-    laStringSplitor *ss = strSplitPath(Path);
+    laStringSplitor *ss = strSplitPath(Path,0);
     laStringPart *sp = ss ? ss->parts.pFirst : 0;
     int InstanceNum = 0;
 
@@ -779,6 +779,7 @@ laProp *la_CreateProperty(laPropContainer *Container, int Type, const char *Iden
     p->UDFOnly = (Tag & LA_UDF_ONLY) ? 1 : 0;
     p->ReadOnly = (Tag & LA_READ_ONLY) ? 1 : 0;
     p->IsRadAngle = (Tag & LA_RAD_ANGLE)? 1 : 0;
+    p->UDFHideInSave = (Tag & LA_HIDE_IN_SAVE)? 1 : 0;
     if(p->IsRadAngle&&(!p->Unit)) p->Unit="°"; 
 
     return p;
@@ -2009,6 +2010,10 @@ void laMarkMemChanged(void* memuser){
     int level; laMemNodeHyper* m=memGetHead(memuser,&level); if(level!=2) return;
     m->Modified=1;
 }
+void laMarkMemClean(void* memuser){
+    int level; laMemNodeHyper* m=memGetHead(memuser,&level); if(level!=2) return;
+    m->Modified=0;
+}
 
 laPropContainer *la_SetGeneralRoot(laPropContainer **GeneralRoot, const char *Identifier, const char *Name, const char *Description){
     laPropContainer* ret =memAcquire(sizeof(laPropContainer));
@@ -3569,12 +3574,7 @@ laUDF *laOpenUDF(char *FileName, int ReadToMemory, laUDFRegistry* ReadRegistryRe
     u64bit extensions;
     char FilePath[1024]={0};
 
-    if(UseManaged){
-        laManagedUDF* m=la_EnsureManagedUDF(FileName, 0);
-        if(!m->udf) m->udf=memAcquire(sizeof(laUDF));
-        udf=m->udf; udf->Managed=1;
-        *UseManaged=m;
-    }else{ udf=memAcquire(sizeof(laUDF)); }
+    udf=memAcquire(sizeof(laUDF));
 
     strSafeSet(&udf->FileName, FileName);
 
@@ -3584,7 +3584,12 @@ laUDF *laOpenUDF(char *FileName, int ReadToMemory, laUDFRegistry* ReadRegistryRe
     if(ReadToMemory){ la_ReadUDFToMemory(udf); fclose(udf->DiskFile); udf->DiskFile = 0; }
 
     la_ReadBuffer(udf, sizeof(LA_UDF_IDENTIFIER) - 1, Identifier);
-    if (!strSame(Identifier, LA_UDF_IDENTIFIER)){ laCloseUDF(udf); return 0; }
+    if (!strSame(Identifier, LA_UDF_IDENTIFIER)){ laCloseUDF(udf); logPrintNew("\"%s\" is not a UDF file.\n"); return 0; }
+
+    if(UseManaged){
+        laManagedUDF* m=la_EnsureManagedUDF(FileName, 0);
+        if(!m->udf) m->udf=udf; udf->Managed=1; *UseManaged=m;
+    }
 
     extensions = la_ReadPointer(udf);
 
@@ -3681,7 +3686,9 @@ void laRefreshUDFResourcesIn(char* rootpath){
 
     for(int i=0;i<NumFiles;i++){
         struct dirent* d = NameList[i]; int dlen;
-        if((dlen=strlen(d->d_name))<=4 || strcmp(&d->d_name[dlen-4], ".udf")){continue;}
+        char *format = strGetLastSegment(d->d_name, '.'); int file_okay=0;
+        for(laExtensionType* et=MAIN.ExtraExtensions.pFirst;et;et=et->Item.pNext){ if(et->FileType==LA_FILETYPE_UDF && strSame(et->Extension,format)){file_okay=1;break;} }
+
         struct stat s;
         sprintf(Final, "%s%s",rootpath,d->d_name);
         stat(Final, &s);

+ 5 - 0
la_data.h

@@ -202,6 +202,7 @@ STRUCTURE(laProp){
     char UDFIgnore;
     char UDFOnly;
     char UDFIsSingle;
+    char UDFHideInSave;
     char UDFSingleManageable;
     char IsRadAngle;
 
@@ -674,6 +675,7 @@ STRUCTURE(laDiffPost){
 #define LA_AS_IDENTIFIER (1<<25)
 #define LA_UDF_USE_LINK_NODE (1<<26)
 #define LA_READ_ONLY (1<<27)
+#define LA_HIDE_IN_SAVE (1<<28)
 
 STRUCTURE(laThreadNotifier){
     laListItem Item;
@@ -686,6 +688,8 @@ NEED_STRUCTURE(laOperatorType)
 NEED_STRUCTURE(laOperator)
 NEED_STRUCTURE(laEvent)
 
+void laset_InstanceUID(void* instance, char* buf);
+
 void *laget_ListNext(laListItem *Item, void *UNUSED);
 void *laget_ListPrev(laListItem *Item, void *UNUSED);
 void *laget_List2Next(laListItem2 *Item, void *UNUSED);
@@ -755,6 +759,7 @@ int laSetRaw(laPropPack *pp, void* data, int _size);
 
 void laMarkPropChanged(laPropPack* pp);
 void laMarkMemChanged(void* memuser);
+void laMarkMemClean(void* memuser);
 
 void *la_FindMatchingInstance(void *From, laProp *Sub, laProp *p, laPropStep *Value);
 

+ 39 - 9
la_interface.h

@@ -26,10 +26,6 @@
 #define LA_VERSION_SUB 0
 #define LA_VERSION_MINOR 0
 
-#define LA_UDF_CAPABILITY_MAIN 5
-#define LA_UDF_CAPABILITY_SUB 0
-#define LA_UDF_CAPABILITY_MINOR 0
-
 #define LA_ACTUATOR_CAPABILITY_MAIN 5
 #define LA_ACTUATOR_CAPABILITY_SUB 0
 #define LA_ACTUATOR_CAPABILITY_MINOR 0
@@ -236,6 +232,12 @@ typedef laBaseNodeType* (*laGetBaseNodeTypeF)(char* str);
 
 NEED_STRUCTURE(laConfirmData);
 
+STRUCTURE(laExtraPreferencePage){
+    laListItem Item;
+    const char* Name;
+    laUiDefineFunc Func;
+};
+
 STRUCTURE(LA){
     laListItem Hyper;
 
@@ -258,6 +260,8 @@ STRUCTURE(LA){
     int ReTriggerOperators;
     laPanel* DockingPanel;
 
+    char SysFontDir[512];
+
     char *InputBuf; int InputBufNext, InputBufMax;
     int32_t *InputBufU; int InputBufUNext, InputBufUMax;
     Atom MsgDelWindow;
@@ -293,6 +297,14 @@ STRUCTURE(LA){
 
     laHash256 OperatorTypeHash;
 
+    laListHandle ExtraExtensions;
+    laListHandle ExtraPreferencePaths;
+    laListHandle ExtraPreferencePages;
+    laUiDefineFunc PreferencePageDisplay;
+    laUiDefineFunc PreferencePageInput;
+    laUiDefineFunc PreferencePageResource;
+    laUiDefineFunc PreferencePageTheme;
+
     laPropContainer *GeneralIntSub;
     laPropContainer *GeneralFloatSub;
     laPropContainer *GeneralEnumSub;
@@ -330,6 +342,9 @@ STRUCTURE(LA){
     laListHandle View2DTemplates;
     laUiDefineFunc MenuButtons;
     laUiDefineFunc MenuExtras;
+    laUiDefineFunc AboutContent;
+    laUiDefineFunc AboutVersion;
+    laUiDefineFunc AboutAuthor;
     const char* MenuProgramName;
 
     laHash256 GlobalMemPool;
@@ -1599,6 +1614,12 @@ laUiList *la_FindSubListWithInstance(laUiItem *ui, void *Instance);
 #define LA_FILE_SELECT_FILE 0
 #define LA_FILE_SELECT_FOLDER 1
 
+STRUCTURE(laExtensionType){
+    laListItem Item;
+    int FileType;
+    const char* Extension;
+};
+
 STRUCTURE(laFileItem){
     laListItem Hyper;
     int IsFolder;
@@ -1627,7 +1648,9 @@ STRUCTURE(laFileBrowser){
     int SelectFolder;
     int WarnFileExists;
     int StatusWaitingWarning;
-    char UseFormat[64];
+    char UseExtension[64];
+    int FilterType;
+    laStringSplitor* ss_filter_extensions;
 };
 
 STRUCTURE(laUDFPreviewExtra){
@@ -1652,13 +1675,22 @@ 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();
+void la_ScanWacomDevices(Display *display, int deviceid);
 
+void laSetFontFolderPath(char* absolute);
 int laGetReady();
 void laShutoff();
+void laSaveUserPreferences();
+void laEnsureUserPreferences();
+void laAddExtraExtension(int FileType, ...);
+void laAddExtraPreferencePath(const char* path);
+void laAddExtraPreferencePage(const char* name, laUiDefineFunc Func);
 void laSetFrameCallbacks(laPreFrameF PreFrame, laPreDrawF PreDraw, laPostFrameF PostFrame);
 void laSetCleanupCallback(laCleanupF Cleanup);
+void laSetMenuBarTemplates(laUiDefineFunc MenuButtons, laUiDefineFunc MenuExtras, const char* ProgramName);
+void laSetAboutTemplates(laUiDefineFunc AboutContent, laUiDefineFunc AboutVersion, laUiDefineFunc AboutAuthor);
+void laSetPreferenceTemplates(laUiDefineFunc PreferencePageDisplay, laUiDefineFunc PreferencePageInput, laUiDefineFunc PreferencePageResource, laUiDefineFunc PreferencePageTheme);
+
 laWindow *laDesignWindow(int X, int Y, int W, int H);
 laLayout *laDesignLayout(laWindow *w, char *Title);
 void laFoldBlockTitle(laBlock* b);
@@ -1855,8 +1887,6 @@ laUiList *la_DetectUiListRecursiveDeep(laUiList *uil, int x, int y, int LimH, la
 
 int laStartWindow(laWindow *w);
 
-void laSetMenuBarTemplates(laUiDefineFunc MenuButtons, laUiDefineFunc MenuExtras, const char* ProgramName);
-
 void laMainLoop();
 
 void la_DestroyOperatorType(laOperatorType* at);

+ 51 - 13
la_kernel.c

@@ -67,7 +67,7 @@ static void la_RegisterWacomEventMasks(Display *display, int deviceid)
 	logPrint("    Device Name: '%s' (%d)\n", dev->name, dev->deviceid);
 	//la_PrintWacomValuators(display, dev->classes, dev->num_classes);
 }
-static void la_ScanWacomDevices(Display *display, int deviceid){
+void la_ScanWacomDevices(Display *display, int deviceid){
     XIDeviceInfo *info, *dev;
     int ndevices;
     int i; char * word;
@@ -283,6 +283,10 @@ void logClear(){
 
 //=======================
 
+void laSetFontFolderPath(char* absolute){
+    strcpy(MAIN.SysFontDir,absolute); int len=strlen(MAIN.SysFontDir);
+    if(MAIN.SysFontDir[len-1]!='/'){ MAIN.SysFontDir[len]='/'; MAIN.SysFontDir[len+1]=0; }
+}
 int laGetReady(){
     Window root, win;
     GLint att[] = {GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None};
@@ -391,15 +395,16 @@ int laGetReady(){
     tnsInitBuiltinShaders();
 
     tnsSetuptnsFontManager();
-    tnsLoadSystemFont(4,
-        "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
-        "/home/yiming/.local/share/fonts/NotoEmoji-Regular.ttf",
-        "/home/yiming/.local/share/fonts/NotoSansSymbols-Regular.ttf",
-        "/home/yiming/.local/share/fonts/NotoSansSymbols2-Regular.ttf"
-        //"/home/yiming/.local/share/fonts/NotoMusic-Regular.ttf",
-        //"/home/yiming/.local/share/fonts/NotoSansEgyptianHieroglyphs-Regular.ttf"
-        );
-    tnsLoadSystemFontMono("/home/yiming/.local/share/fonts/NotoSansMono-Regular.ttf");
+#define LOAD_FONT(font)\
+    if(!tnsLoadSystemFont(MAIN.SysFontDir, font)) printf("Can't load font \"" font "\"\n");
+
+    LOAD_FONT("NotoSansCJK-Regular.ttc");
+    LOAD_FONT("NotoEmoji-Regular.ttf");
+    LOAD_FONT("NotoSansSymbols-Regular.ttf");
+    LOAD_FONT("NotoSansSymbols2-Regular.ttf");
+    //LOAD_FONT("NotoMusic-Regular.ttf");
+    //LOAD_FONT("NotoSansEgyptianHieroglyphs-Regular.ttf);
+    if(!tnsLoadSystemFontMono(MAIN.SysFontDir, "NotoSansMono-Regular.ttf")) printf("Can't load font \"NotoSansMono-Regular.ttf\"\n");;
 
     arrEnsureLength(&MAIN.InputBuf,0,&MAIN.InputBufMax,sizeof(char));
     arrEnsureLength(&MAIN.InputBufU,0,&MAIN.InputBufUMax,sizeof(uint32_t));
@@ -443,6 +448,8 @@ int laGetReady(){
 
     laSetMenuBarTemplates(laui_DefaultMenuButtons, laui_DefaultMenuExtras, "🧩LaGUI 2022");
 
+    laAddExtraExtension(LA_FILETYPE_UDF,"udf",0);
+
     //tns_RegisterResourcesForSoftwareRender();
     la_RegisterGeneralProps();
     la_RegisterBuiltinTemplates();
@@ -523,6 +530,9 @@ void laSaveUserPreferences(){
     char path[1024]; sprintf(path,"%s%s",MAIN.WorkingDirectory->Ptr,"preferences.udf");
     laUDF* udf=laPrepareUDF(path);
     laWriteProp(udf,"la.user_preferences");
+    for(laListItemPointer* lip=MAIN.ExtraPreferencePaths.pFirst;lip;lip=lip->pNext){
+        laWriteProp(udf,lip->p);
+    }
     laPackUDF(udf,0);
 }
 void laEnsureUserPreferences(){
@@ -535,6 +545,37 @@ void laEnsureUserPreferences(){
     laCloseUDF(udf);
 }
 
+void laAddExtraExtension(int FileType, ...){
+    va_list list; va_start(list,FileType);
+    char* ext;
+    while(ext=va_arg(list,char*)){ if(!ext) break;
+        laExtensionType* et=memAcquireSimple(sizeof(laExtensionType));
+        et->FileType=FileType; et->Extension=ext;
+        lstAppendItem(&MAIN.ExtraExtensions, et);
+    }
+    va_end(list);
+}
+void laAddExtraPreferencePath(const char* path){
+    if(!path) return;
+    lstAppendPointer(&MAIN.ExtraPreferencePaths, path);
+}
+void laAddExtraPreferencePage(const char* name, laUiDefineFunc Func){
+    if(!name || !Func) return;
+    laExtraPreferencePage*epp=memAcquireSimple(sizeof(laExtraPreferencePage));
+    epp->Name=name; epp->Func=Func; lstAppendItem(&MAIN.ExtraPreferencePages,epp);
+}
+
+void laSetMenuBarTemplates(laUiDefineFunc MenuButtons, laUiDefineFunc MenuExtras, const char* ProgramName){
+    MAIN.MenuButtons=MenuButtons; MAIN.MenuExtras=MenuExtras; MAIN.MenuProgramName=ProgramName;
+}
+void laSetAboutTemplates(laUiDefineFunc AboutContent, laUiDefineFunc AboutVersion, laUiDefineFunc AboutAuthor){
+    MAIN.AboutAuthor=AboutAuthor; MAIN.AboutVersion=AboutVersion; MAIN.AboutContent=AboutContent;
+}
+void laSetPreferenceTemplates(laUiDefineFunc PreferencePageDisplay, laUiDefineFunc PreferencePageInput, laUiDefineFunc PreferencePageResource, laUiDefineFunc PreferencePageTheme){
+    MAIN.PreferencePageDisplay=PreferencePageDisplay; MAIN.PreferencePageInput=PreferencePageInput;
+    MAIN.PreferencePageResource=PreferencePageResource; MAIN.PreferencePageTheme=PreferencePageTheme;
+}
+
 
 //MSG====================================================
 
@@ -1613,9 +1654,6 @@ void la_ClearUnusedFramebuffers(laWindow* w){
     }
 }
 
-void laSetMenuBarTemplates(laUiDefineFunc MenuButtons, laUiDefineFunc MenuExtras, const char* ProgramName){
-    MAIN.MenuButtons=MenuButtons; MAIN.MenuExtras=MenuExtras; MAIN.MenuProgramName=ProgramName;
-}
 void la_WindowDefDraw(laWindow *w, laBoxedTheme *bt){
     laPanel *p, *NextP;
     laLayout *l = w->CurrentLayout;

+ 2 - 2
la_tns.h

@@ -1195,8 +1195,8 @@ void tnsRecalculateFaceAverageNormal(tnsFace *f);
 int tnsLoadVectorGraphPackage(const char *name, unsigned int size);
 int tnsGetMonoFontAdvance();
 int tnsInvalidateFontCache();
-void tnsLoadSystemFontMono(char* mono);
-void tnsLoadSystemFont(int num_fonts, ...);
+int tnsLoadSystemFontMono(char* from, char* mono);
+int tnsLoadSystemFont(char* from, char* name);
 int tnsGetTextureMemoryComponetCount(tnsTexture *t);
 
 void tnsUseNormal(int Use);

+ 41 - 51
la_tns_kernel.c

@@ -2763,58 +2763,59 @@ int tnsInvalidateFontCache(){
     glClearTexImage(f->TexBuffer.GLTexHandle, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
 }
 tnsFontSingleCharacter *tfntFetchCharTextureIDW(uint32_t ch, int UseMono);
-void tnsLoadSystemFontMono(char* mono){
-    if(!FM->UsingFont || !mono || !mono[0]) return;
+
+const char* TNS_FONT_LOAD_OPTIONS[9]={"./","fonts/","lagui/fonts/","../","../fonts/","../lagui/fonts/","../../","../../fonts/","../../lagui/fonts/"};
+int tnsLoadSystemFontMono(char* from, char* mono){
+    char buf[1024]; if(!FM->UsingFont || !mono || !mono[0]) return 0;
     tnsFont *f=FM->UsingFont;
-    
     int GenHeight=LA_RH*MAIN.FontSize;
 
     int full_adv=0,half_adv=0;
     for(int i=0;i<f->NumFaces;i++){
         if(!FT_Get_Advance(f->ftface[i],L'我',FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP, &full_adv)) break;
     }
-
-    if (FT_New_Face(f->ftlib, mono, 0, &f->ftfacemono)) SEND_PANIC_ERROR("Freetype Can't Init Face");
-    FT_Select_Charmap(f->ftfacemono, FT_ENCODING_UNICODE);
-    FT_Set_Char_Size(f->ftfacemono, 0, GenHeight << 6, 96, 96);
-    if(!FT_Get_Advance(f->ftfacemono,'a',FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP, &half_adv)){
-        f->MonoScale=(real)full_adv/(half_adv*2);
-        FT_Set_Char_Size(f->ftfacemono, 0, (GenHeight << 6)*f->MonoScale, 96, 96);
-        logPrint("Monospace font scale: %.2f\n",f->MonoScale); FT_Glyph glyph;
-        if (FT_Load_Char(f->ftfacemono, 'a', FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)||
-            FT_Get_Glyph(f->ftfacemono->glyph, &glyph)){ SEND_PANIC_ERROR("Monospace font doesn't contain character 'a'"); }
-        f->MonoAdvance=(real)f->ftfacemono->glyph->advance.x/64.0;
-        if(glyph) FT_Done_Glyph(glyph);
+    for(int i=-1;i<9;i++){
+        char* option=(i<0)?from:TNS_FONT_LOAD_OPTIONS[i]; sprintf(buf,"%s%s",option,mono);
+        if (FT_New_Face(f->ftlib, buf, 0, &f->ftfacemono)) continue;
+        FT_Select_Charmap(f->ftfacemono, FT_ENCODING_UNICODE);
+        FT_Set_Char_Size(f->ftfacemono, 0, GenHeight << 6, 96, 96);
+        if(!FT_Get_Advance(f->ftfacemono,'a',FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP, &half_adv)){
+            f->MonoScale=(real)full_adv/(half_adv*2);
+            FT_Set_Char_Size(f->ftfacemono, 0, (GenHeight << 6)*f->MonoScale, 96, 96);
+            logPrint("Monospace font scale: %.2f\n",f->MonoScale); FT_Glyph glyph;
+            if (FT_Load_Char(f->ftfacemono, 'a', FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)||
+                FT_Get_Glyph(f->ftfacemono->glyph, &glyph)){ SEND_PANIC_ERROR("Monospace font doesn't contain character 'a'"); }
+            f->MonoAdvance=(real)f->ftfacemono->glyph->advance.x/64.0;
+            if(glyph) FT_Done_Glyph(glyph);
+        }
+        return 1;
     }
+    return 0;
 };
-void tnsLoadSystemFont(int num_fonts, ...){
-    char * name;
-
-    tnsFont *f = CreateNew(tnsFont);
-    f->characters=calloc(1, sizeof(tnsFontSingleCharacter*)*TNS_UNICODE_COUNT);
-    f->monocharacters=calloc(1, sizeof(tnsFontSingleCharacter*)*TNS_MONO_COUNT);
+int tnsLoadSystemFont(char* from, char* name){
+    char buf[1024];
+
+    tnsFont *f=FM->UsingFont; if(!f){
+        f = CreateNew(tnsFont);
+        f->characters=calloc(1, sizeof(tnsFontSingleCharacter*)*TNS_UNICODE_COUNT);
+        f->monocharacters=calloc(1, sizeof(tnsFontSingleCharacter*)*TNS_MONO_COUNT);
+        f->height = LA_RH*(MAIN.FontSize/2.0f+0.5f);
+        tnsInit2DTexture(&f->TexBuffer, GL_RED, TNS_FONT_BUFFER_W, TNS_FONT_BUFFER_H, 0);
+        lstAppendItem(&FM->Fonts, f); FM->UsingFont=f;
+        if (FT_Init_FreeType(&f->ftlib)) SEND_PANIC_ERROR("Freetype Init Failed!");
+    }
 
-    f->height = LA_RH*(MAIN.FontSize/2.0f+0.5f);
     int GenHeight=LA_RH*MAIN.FontSize;
-
-    if (FT_Init_FreeType(&f->ftlib)) SEND_PANIC_ERROR("Freetype Init Failed!");
-
-    if(num_fonts>=16){num_fonts=16;}
-    va_list fn;
-    va_start(fn, num_fonts);
-    for(int i=0;i<num_fonts;i++){
-        name = va_arg(fn, char*);
-        if (name && FT_New_Face(f->ftlib, name, 0, &f->ftface[i])) SEND_PANIC_ERROR("Freetype Can't Init Face");
-        FT_Select_Charmap(f->ftface[i], FT_ENCODING_UNICODE);
-        FT_Set_Char_Size(f->ftface[i], 0, GenHeight << 6, 96, 96);
+    for(int i=-1;i<9;i++){
+        char* option=(i<0)?from:TNS_FONT_LOAD_OPTIONS[i]; sprintf(buf,"%s%s",option,name);
+        if (FT_New_Face(f->ftlib, buf, 0, &f->ftface[f->NumFaces])) continue;
+        FT_Select_Charmap(f->ftface[f->NumFaces], FT_ENCODING_UNICODE);
+        FT_Set_Char_Size(f->ftface[f->NumFaces], 0, GenHeight << 6, 96, 96);
+        f->NumFaces++;
+        return 1;
     }
-    va_end(fn);
-    f->NumFaces = num_fonts;
-    
-    tnsInit2DTexture(&f->TexBuffer, GL_RED, TNS_FONT_BUFFER_W, TNS_FONT_BUFFER_H, 0);
-    
-    lstAppendItem(&FM->Fonts, f);
-    tnsUseFont(0);
+
+    return 0;
 };
 int tnsLoadVectorGraphPackage(const char *name, unsigned int size){
     char i;
@@ -3015,17 +3016,6 @@ int CMP_NAME_IsThisFont(tnsFont *enumed, char *name){
     return (!strcmp(enumed->fontName, name));
 };
 
-int tnsUseFont(char *name){
-    tnsFont *f;
-    if(!name){f = FM->Fonts.pFirst;}else{ f = lstFindItem(name, CMP_NAME_IsThisFont, &FM->Fonts); }
-
-    if (!f) return 0;
-
-    FM->UsingFont = f;
-
-    return 1;
-};
-
 int tnsStringGetDimension(char* content, uint32_t* contentU, int Count, int WLimit, int* Rows, int UseMono){
     real sx = 0; int sy = FM->UsingFont->height; real MA=FM->UsingFont->MonoAdvance;
     int i, rows=1, advance=1;

+ 3 - 3
la_util.c

@@ -1418,13 +1418,13 @@ void strToLowerCase(char *Str){
     }
 }
 
-laStringSplitor *strSplitPath(char *path){
+laStringSplitor *strSplitPath(char *path,char terminator){
     laStringPart *sp;
     laStringSplitor *ss;
     char *pivot = path;
     char *temp_result;
-    char Type = L'.';
-    char NextType = L'.';
+    char Type = terminator?terminator:'.';
+    char NextType = '.';
 
     if (!path || !path[0]) return 0;
 

+ 1 - 1
la_util.h

@@ -567,7 +567,7 @@ void strReplaceCharacter(char * Str, char Find, char Replace);
 void strToUpperCase(char * Str);
 void strToLowerCase(char * Str);
 
-laStringSplitor* strSplitPath(char * path);
+laStringSplitor *strSplitPath(char *path,char terminator);
 int strMakeInstructions(laStringSplitor** result,char * content);
 laStringPart* strGetArgument(laStringSplitor* ss, char * content);
 char * strGetArgumentString(laStringSplitor* ss, char * content);

+ 45 - 15
resources/la_operators.c

@@ -54,12 +54,10 @@ int OPINV_PureYesNo(laOperator *a, laEvent *e){
 }
 
 #define DEFINE_FORMAT(str, type) \
-    if (!strcmp(format, str))    \
-        return type;
-int la_DetectFileItemType(laFileItem *fi){
-    char *format = strGetLastSegment(fi->Name, '.');
+    if (!strcmp(format, str)) return type;
+int la_DetectFileItemType(char* format){
+    for(laExtensionType* et=MAIN.ExtraExtensions.pFirst;et;et=et->Item.pNext){ DEFINE_FORMAT(et->Extension, et->FileType); }
 
-    DEFINE_FORMAT("udf", LA_FILETYPE_UDF);
     DEFINE_FORMAT("lasdexchange", LA_FILETYPE_LASDEXCHANGE);
 
     DEFINE_FORMAT("odt", LA_FILETYPE_DOCUMENT);
@@ -151,6 +149,18 @@ int la_DetectFileItemType(laFileItem *fi){
 
     return 0;
 }
+int la_AcceptFileFormat(laFileBrowser* fb, char* format){
+    if(!format || !format[0]) return 1;
+    if(fb->FilterType){
+        for(laExtensionType* et=MAIN.ExtraExtensions.pFirst;et;et=et->Item.pNext){ if(et->FileType==fb->FilterType && strSame(format, et->Extension)) return 1; }
+        return 0;
+    }
+    if(!fb->ss_filter_extensions) return 1;
+    for(laStringPart* sp=fb->ss_filter_extensions->parts.pFirst;sp;sp=sp->Item.pNext){
+        if(!strcmp(sp->Content, format)) return 1;
+    }
+    return 0;
+}
 void la_FileBrowserRebuildList(laFileBrowser *fb){
     laFileItem *fi = 0;
     laDiskItem *dl = 0;
@@ -187,10 +197,13 @@ void la_FileBrowserRebuildList(laFileBrowser *fb){
             fi->Type = LA_FILETYPE_FOLDER;
             lstAppendItem(&fb->FileList, fi);
         }elif (!fb->SelectFolder){
+            char *format = strGetLastSegment(d->d_name, '.');
+            if(!la_AcceptFileFormat(fb,format)){ continue; }
+
             fi = memAcquireSimple(sizeof(laFileItem));
             strcpy(fi->Name, d->d_name);
             fi->Size = s.st_size;
-            fi->Type = la_DetectFileItemType(fi);
+            fi->Type = la_DetectFileItemType(format);
 
             struct tm *t = localtime(&s.st_ctime);
             fi->TimeModified.Year = t->tm_year+1900;
@@ -234,18 +247,21 @@ void la_FileBrowserRebuildList(laFileBrowser *fb){
     //}
 
     fb->Active = 0;
-
-    fb->FileName[0] = 0;
 }
 laFileBrowser *la_FileBrowserInit(laOperator *a){
     laFileBrowser *fb = memAcquireHyper(sizeof(laFileBrowser));
+    char* arg=0;
 
     strcpy(fb->Path, MAIN.WorkingDirectory->Ptr);
 
     if (strArgumentMatch(a->ExtraInstructionsP, "select", "folder")){ fb->SelectFolder = LA_FILE_SELECT_FOLDER; }
     if (strArgumentMatch(a->ExtraInstructionsP, "warn_file_exists", "true")){ fb->WarnFileExists = 1; }
+    if ((arg=strGetArgumentString(a->ExtraInstructionsP, "filter_extensions"))){ fb->ss_filter_extensions=strSplitPath(arg,','); }
+    if ((arg=strGetArgumentString(a->ExtraInstructionsP, "use_extension"))){ strcpy(fb->UseExtension, arg); }
+    if ((arg=strGetArgumentString(a->ExtraInstructionsP, "filter_type"))){ sscanf(arg,"%d",&fb->FilterType); }
 
     la_FileBrowserRebuildList(fb);
+    fb->FileName[0] = 0;
 
     return fb;
 }
@@ -257,6 +273,7 @@ void laset_FileBrowserSelectFile(laFileBrowser *fb, laFileItem *fi, int State){
             if (fb->Path[len - 1] != L'/') strcat(fb->Path, "/");
             strcat(fb->Path, fi->Name);
             la_FileBrowserRebuildList(fb);
+            fb->FileName[0] = 0;
             laRecalcCurrentPanelImmediate();
         }
     }else{
@@ -297,9 +314,14 @@ void *laset_FileBrowserActiveDisk(laFileBrowser *fb, laDiskItem *di, int UNUSED_
     fb->RootDisk = di;
     laget_FileBrowserDiskID(di, fb->Path);
     la_FileBrowserRebuildList(fb);
+    fb->FileName[0] = 0;
 }
 void laset_FileBrowserPath(laFileBrowser *fb, char *content){
-    strCopyFull(fb->Path, content);
+    strCopyFull(fb->Path, content); la_FileBrowserRebuildList(fb); fb->FileName[0] = 0; laRecalcCurrentPanel();
+}
+void laset_FileBrowserFileName(laFileBrowser *fb, char *content){
+    strCopyFull(fb->FileName, content);
+    if(fb->UseExtension[0] && strcmp(strGetLastSegment(fb->FileName,'.'),fb->UseExtension)){ strcat(fb->FileName,"."); strcat(fb->FileName,fb->UseExtension); }
     la_FileBrowserRebuildList(fb);
     laRecalcCurrentPanel();
 }
@@ -315,7 +337,7 @@ void la_FileBrowserUpLevel(laFileBrowser *fb){
     }
     if (Count > 1) *LastP = 0;
     else if(LastP) *(LastP + 1) = 0;
-    la_FileBrowserRebuildList(fb);
+    la_FileBrowserRebuildList(fb); fb->FileName[0] = 0;
 }
 int OPINV_FileBrowser(laOperator *a, laEvent *e){
     a->CustomData = la_FileBrowserInit(a);
@@ -405,7 +427,7 @@ int OPEXT_UDFOperation(laOperator *a, laEvent *e){
     memFree(upe);
 }
 int OPINV_UDFAppend(laOperator *a, laEvent *e){
-    laInvoke(a, "LA_file_dialog", e, 0, 0, 0);
+    laInvoke(a, "LA_file_dialog", e, 0, "filter_type=1;", 0);
     a->CustomData = memAcquireSimple(sizeof(laUDFPreviewExtra));
     return LA_RUNNING;
 }
@@ -550,6 +572,14 @@ int OPINV_RemoveResourceFolder(laOperator *a, laEvent *e){
     return LA_FINISHED;
 }
 
+int OPCHK_Undo(laPropPack *This, laStringSplitor *ss){
+    laDiff* diff=MAIN.HeadDifference; if(!diff) return 0;
+    diff=diff->Item.pPrev; if(!diff) return 0;
+}
+int OPCHK_Redo(laPropPack *This, laStringSplitor *ss){
+    laDiff* diff=MAIN.HeadDifference; if(!diff) return 0;
+    if(diff==MAIN.Differences.pLast) return 0;
+}
 int OPINV_Undo(laOperator *a, laEvent *e){
     laUndo();
     laPrintDBInstInfo();
@@ -1881,8 +1911,8 @@ void la_RegisterBuiltinOperators(){
     laCreateOperatorType("LA_terminate_program", "Quit", "Terminate Program Immediately",
                           OPCHK_TerminateProgram, 0, 0, OPINV_TerminateProgram, 0, L'⏻', LA_ACTUATOR_SYSTEM);
 
-    laCreateOperatorType("LA_undo", "Undo", "Undo from recorded data state", 0, 0, 0, OPINV_Undo, 0, L'⮌', LA_ACTUATOR_SYSTEM);
-    laCreateOperatorType("LA_redo", "Redo", "Redo using recorded data state", 0, 0, 0, OPINV_Redo, 0, L'⮎', LA_ACTUATOR_SYSTEM);
+    laCreateOperatorType("LA_undo", "Undo", "Undo from recorded data state", OPCHK_Undo, 0, 0, OPINV_Undo, 0, L'⮌', LA_ACTUATOR_SYSTEM);
+    laCreateOperatorType("LA_redo", "Redo", "Redo using recorded data state", OPCHK_Redo, 0, 0, OPINV_Redo, 0, L'⮎', LA_ACTUATOR_SYSTEM);
 
     laCreateOperatorType("LA_translation_dump", "Dump Untranslated Text", "Dump Untranslated Text To File", 0, 0, 0, OPINV_TranslationDumpMisMatch, 0, L'📥', LA_ACTUATOR_SYSTEM);
     laCreateOperatorType("LA_open_internet_link", "Goto", "Open Internet Link", 0, 0, 0, OPINV_OpenInternetLink, 0, L'🌐', LA_ACTUATOR_SYSTEM);
@@ -1966,7 +1996,7 @@ void la_RegisterBuiltinOperators(){
     _LA_PROP_FILE_BROWSER = pc;
 
     laAddStringProperty(pc, "path", "Path", "Directort Path", 0, 0, 0, "/", 0, offsetof(laFileBrowser, Path), 0, 0, laset_FileBrowserPath, 0, LA_UDF_LOCAL);
-    laAddStringProperty(pc, "file_name", "File Name", "File Name", 0, 0, 0, 0, 0, offsetof(laFileBrowser, FileName), 0, 0, 0, 0, LA_UDF_LOCAL);
+    laAddStringProperty(pc, "file_name", "File Name", "File Name", 0, 0, 0, 0, 0, offsetof(laFileBrowser, FileName), 0, 0, laset_FileBrowserFileName, 0, LA_UDF_LOCAL);
     laAddSubGroup(pc, "file_list", "File List", "List Of Files And Directories Under A Specific Path", "file_item",0,0,laui_FileBrowserFileItem, -1, 0, laget_FileBrowserActiveFile, 0, 0, 0, laset_FileBrowserSelectFile, offsetof(laFileBrowser, FileList), 0);
     laAddSubGroup(pc, "disk_list", "Disk List", "List Of All Logical Drives (In Windows)", "disk_item",0, 0, 0, -offsetof(laFileBrowser, RootDisk), 0, 0, 0, 0, 0, laset_FileBrowserActiveDisk, offsetof(laFileBrowser, Disks), 0);
     ep = laAddEnumProperty(pc, "select_what", "Select What", "Select folder or file", 0, 0, 0, 0, 0, offsetof(laFileBrowser, SelectFolder), 0, 0, 0, 0, 0, 0, 0, 0, 0,LA_READ_ONLY);{
@@ -2013,7 +2043,7 @@ void la_RegisterBuiltinOperators(){
 
     at = laCreateOperatorType("LA_udf_save_instance", "Save Instance", "Save a instance as a UDF block", 0, 0, OPEXT_UDFOperation, OPINV_UDFSaveInstance, OPMOD_UDFSaveInstance, L'📑', LA_ACTUATOR_SYSTEM);
 
-    at = laCreateOperatorType("LA_managed_save", "Managed Save", "Save managed data blocks", 0, 0, OPEXT_ManagedSave, OPINV_ManagedSave, OPMOD_ManagedSave, L'🖫', LA_ACTUATOR_SYSTEM);
+    at = laCreateOperatorType("LA_managed_save", "Save as", "Save managed data blocks", 0, 0, OPEXT_ManagedSave, OPINV_ManagedSave, OPMOD_ManagedSave, L'🖫', LA_ACTUATOR_SYSTEM);
     pc = laDefineOperatorProps(at, 1);
     ep=laAddEnumProperty(pc, "show_page", "Show Page", "Show whether data blocks or UDF files", 0,0,0,0,0,offsetof(laManagedSaveExtra, ShowPage),0,laset_ManagedSavePage,0,0,0,0,0,0,0,0);
     laAddEnumItemAs(ep, "DATA_BLOCKS", "Data Blocks", "All data blocks", 0, 0);

+ 3 - 0
resources/la_properties.c

@@ -1060,6 +1060,9 @@ void la_RegisterInternalProps(){
                 laAddEnumItemAs(ep, "DATA_BLOCKS", "Data Blocks", "All data blocks", 0, 0);
                 laAddEnumItemAs(ep, "FILES", "Files", "All Files", 1, 0);
             }
+        
+            laAddIntProperty(p, "wacom_device_stylus", "Stylus Device", "Wacom stylus device ID", LA_WIDGET_INT_PLAIN, 0, 0, 0, 0, 0, 0, 0, offsetof(LA, WacomDeviceStylus), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, LA_READ_ONLY);
+            laAddIntProperty(p, "wacom_device_eraser", "Eraser Device", "Wacom eraser device ID", LA_WIDGET_INT_PLAIN, 0, 0, 0, 0, 0, 0, 0, offsetof(LA, WacomDeviceEraser), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, LA_READ_ONLY);
         }
 
         p = laAddPropertyContainer("la_translation_language", "Language", "Translation language pack", 0, 0, sizeof(laTranslationNode), 0, 0, 2);{

+ 75 - 70
resources/la_templates.c

@@ -464,27 +464,37 @@ void laui_ColumnItem(laUiList *uil, laPropPack *Base, laPropPack *UNUSED_This, l
 void laui_DefaultMenuButtonsFileEntries(laUiList *uil, laPropPack *pp, laPropPack *actinst, laColumn *extracol, int context){
     laColumn* c=laFirstColumn(uil);
             
-    laShowLabel(uil, c, "Function Test", 0, 0);
+    laShowLabel(uil, c, "Function Test", 0, 0)->Flags|=LA_TEXT_MONO|LA_UI_FLAGS_DISABLED;
     laShowItem(uil, c, 0, "LA_pure_yes_no");
     laShowItem(uil, c, 0, "LA_file_dialog");
     laShowItem(uil, c, 0, "LA_udf_read");
     laShowItem(uil, c, 0, "LA_managed_save");
     laShowItem(uil, c, 0, "LA_manage_udf");
 
-    laShowLabel(uil, c, "Other Entries", 0, 0);
-    laShowItemFull(uil, c, 0, "LA_panel_activator", 0, "panel_id=LAUI_about;text=About;", 0, 0);
+    laShowLabel(uil, c, "Other Entries", 0, 0)->Flags|=LA_TEXT_MONO|LA_UI_FLAGS_DISABLED;
     laShowItem(uil, c, 0, "LA_terminate_program");
 }
+void laui_DefaultMenuButtonsEditEntries(laUiList *uil, laPropPack *pp, laPropPack *actinst, laColumn *extracol, int context){
+    laColumn* c=laFirstColumn(uil);
+    laShowLabel(uil, c, "History", 0, 0)->Flags|=LA_TEXT_MONO|LA_UI_FLAGS_DISABLED;
+    laShowItem(uil, c, 0, "LA_undo")->Flags|=LA_UI_FLAGS_NO_CONFIRM;
+    laShowItem(uil, c, 0, "LA_redo")->Flags|=LA_UI_FLAGS_NO_CONFIRM;
+}
 void laui_DefaultMenuButtonsOptionEntries(laUiList *uil, laPropPack *pp, laPropPack *actinst, laColumn *extracol, int context){
     laColumn* c=laFirstColumn(uil);
-    laShowLabel(uil, c, "Settings", 0, 0);
+    laShowLabel(uil, c, "Settings", 0, 0)->Flags|=LA_TEXT_MONO|LA_UI_FLAGS_DISABLED;
     laShowItemFull(uil, c, 0, "LA_panel_activator", 0, "panel_id=LAUI_user_preferences;", 0, 0);
+    laShowLabel(uil, c, "Information", 0, 0)->Flags|=LA_TEXT_MONO|LA_UI_FLAGS_DISABLED;
+    laShowItemFull(uil, c, 0, "LA_panel_activator", 0, "panel_id=LAUI_about;text=About;", 0, 0);
 }
 void laui_DefaultMenuButtons(laUiList *uil, laPropPack *pp, laPropPack *actinst, laColumn *extracol, int context){
     laUiList *muil; laColumn *mc,*c = laFirstColumn(uil);
     muil = laMakeMenuPage(uil, c, "File");{
         mc = laFirstColumn(muil); laui_DefaultMenuButtonsFileEntries(muil,pp,actinst,extracol,0);
     }
+    muil = laMakeMenuPage(uil, c, "Edit");{
+        mc = laFirstColumn(muil); laui_DefaultMenuButtonsEditEntries(muil,pp,actinst,extracol,0);
+    }
     muil = laMakeMenuPage(uil, c, "Options"); {
         mc = laFirstColumn(muil); laui_DefaultMenuButtonsOptionEntries(muil,pp,actinst,extracol,0);
     }
@@ -947,7 +957,7 @@ void laui_FileBrowserFileList(laUiList *uil, laPropPack *THIS_UNUSED, laPropPack
     laShowItem(u, c, Operator, "disk_list");
 
     b = laMakeGroup(uil, cr, "File List", 0);b->State=LA_UI_ACTIVE; u=b->Page;
-    u->HeightCoeff = -3;
+    u->HeightCoeff = -1;
     c = laFirstColumn(u);
     laShowColumnAdjuster(u, c);
     laSplitColumn(u, c, 0.1);
@@ -1031,10 +1041,9 @@ void laui_ManagedPropInstance(laUiList *uil, laPropPack *Base, laPropPack *Opera
 
     laPropContainer* pc=la_EnsureSubTarget(Base->LastPs->p, Base->EndInstance);
 
-
     if(pc->UDFPropagate){
         char buf[128]; sprintf(buf,"Assign all \"%s\" into:",pc->Name);
-        laShowLabel(uil,crl,buf,0,0)->Flags|=LA_TEXT_ALIGN_RIGHT;
+        laShowLabel(uil,crl,buf,0,0)->Flags|=LA_TEXT_ALIGN_RIGHT|LA_UI_FLAGS_DISABLED;
         laShowItem(uil,crr,Base,"__single_udf_propagate");
     }
     
@@ -1068,7 +1077,7 @@ void laui_ManagedPropInstance(laUiList *uil, laPropPack *Base, laPropPack *Opera
         if(p->PropertyType!=LA_PROP_SUB || p->UDFIsRefer) continue;
         la_EnsureSubTarget(p,0); 
         if(p->SubProp &&p->SubProp->Hyper!=2 &&(!p->UDFIsSingle)) continue;
-        if(p->UDFNoCreate) continue;
+        if(p->UDFNoCreate || p->UDFHideInSave) continue;
         if(p==pc->SaverDummy){
             laShowItem(uil,cl,Base,"__single_saver_dummy.__modified");
             laShowItem(uil,crl,Base,"identifier")->Flags|=LA_UI_FLAGS_PLAIN;
@@ -1196,6 +1205,11 @@ void laui_UserPreference(laUiList *uil, laPropPack *Base, laPropPack *OperatorIn
     laShowItem(uil,cr,0,"LA_save_user_preferences");
 
     bracket = laMakeTab(uil, c, 0);{
+
+        for(laExtraPreferencePage* epp=MAIN.ExtraPreferencePages.pFirst;epp;epp=epp->Item.pNext){
+            muil = laAddTabPage(bracket, epp->Name); epp->Func(muil,0,0,0,0);
+        }
+
         muil = laAddTabPage(bracket, "Display");{
             //muil->HeightCoeff = -1;
             mc = laFirstColumn(muil);
@@ -1203,6 +1217,8 @@ void laui_UserPreference(laUiList *uil, laPropPack *Base, laPropPack *OperatorIn
             mcl = laLeftColumn(mc, 0);
             mcr = laRightColumn(mc, 0);
 
+            if(MAIN.PreferencePageDisplay){ MAIN.PreferencePageDisplay(muil,0,0,0,0); }
+
             laShowLabel(muil, mc, "Interface:", 0, 0);
             laShowLabel(muil, mcl, "Row Height:", 0, 0);
             laShowItem(muil, mcr, 0, "la.user_preferences.interface_size");
@@ -1247,6 +1263,8 @@ void laui_UserPreference(laUiList *uil, laPropPack *Base, laPropPack *OperatorIn
             mcl = laLeftColumn(mc, 0);
             mcr = laRightColumn(mc, 0);
 
+            if(MAIN.PreferencePageInput){ MAIN.PreferencePageInput(muil,0,0,0,0); }
+
             laShowLabel(muil, mc, "User Interactions:", 0, 0);
             laShowItem(muil, mc, 0, "la.user_preferences.scroll_speed");
             laShowItem(muil, mcl, 0, "la.user_preferences.animation_speed");
@@ -1255,6 +1273,21 @@ void laui_UserPreference(laUiList *uil, laPropPack *Base, laPropPack *OperatorIn
             laShowItem(muil, mc, 0, "la.user_preferences.zoom_speed_2d");
             laShowItem(muil, mcl, 0, "la.user_preferences.tooltip_close_distance");
             laShowItem(muil, mcr, 0, "la.user_preferences.idle_time");
+
+            laShowSeparator(muil,mc);
+
+            laShowLabel(muil, mc, "Input device handling:", 0, 0);
+            laShowLabel(muil, mcl, "Input Mapping:", 0, 0)->Flags|=LA_TEXT_ALIGN_RIGHT;
+            laShowItemFull(muil, mcr, 0, "LA_panel_activator",0,"panel_id=LAUI_input_mapper;text=Configure",0,0);
+            laShowLabel(muil, mcl, "Pads/Joysticks:", 0, 0)->Flags|=LA_TEXT_ALIGN_RIGHT;
+            laShowItemFull(muil, mcr, 0, "LA_panel_activator",0,"panel_id=LAUI_controllers;text=Configure",0,0);
+
+            laShowSeparator(muil,mc);
+
+            laShowLabel(muil, mc, "Wacom Devices:", 0, 0);
+            laShowItem(muil, mcl, 0, "la.user_preferences.wacom_device_stylus");
+            laShowItem(muil, mcl, 0, "la.user_preferences.wacom_device_eraser");
+            laShowItemFull(muil, mcr, 0, "LA_refresh_controllers",0,"text=Refresh",0,0);
         }
 
         muil = laAddTabPage(bracket, "Resource");{
@@ -1264,6 +1297,9 @@ void laui_UserPreference(laUiList *uil, laPropPack *Base, laPropPack *OperatorIn
             laSplitColumn(muil, mc, 0.5);
             mcl = laLeftColumn(mc, 0);
             mcr = laRightColumn(mc, 0);
+
+            if(MAIN.PreferencePageResource){ MAIN.PreferencePageResource(muil,0,0,0,0); }
+
             laShowLabel(muil, mcl, "UDF Manager Default View:", 0, 0);
             laShowItem(muil, mcr, 0, "la.user_preferences.manager_default_view")->Flags|=LA_UI_FLAGS_EXPAND;
 
@@ -1279,6 +1315,9 @@ void laui_UserPreference(laUiList *uil, laPropPack *Base, laPropPack *OperatorIn
         muil = laAddTabPage(bracket, "Theme");{
             //muil->HeightCoeff = -1;
             mc = laFirstColumn(muil);
+
+            if(MAIN.PreferencePageTheme){ MAIN.PreferencePageTheme(muil,0,0,0,0); }
+
             laShowLabel(muil, mc, "Active Theme", 0, 0);
             laShowItemFull(muil, mc, 0, "la.themes", LA_WIDGET_COLLECTION_SELECTOR, 0,laui_ThemeListItem,0);
             laShowItemFull(muil, mc, 0, "la.themes", LA_WIDGET_COLLECTION_SINGLE, 0 ,0,0);
@@ -1298,14 +1337,22 @@ void laui_About(laUiList *uil, laPropPack *Base, laPropPack *OperatorInst, laCol
 
     c = laFirstColumn(uil);
 
-    laShowLabel(uil, c, "LaGUI", 0, 0);
-    laShowLabel(uil, c, "A graphical user interface application toolkit", 0, 0)->Flags|=LA_TEXT_LINE_WRAP;
-    laShowLabel(uil, c, "(C)Yiming Wu", 0, 0);
+    if(MAIN.AboutContent){
+        MAIN.AboutContent(uil,Base,OperatorInst,ExtraColumns,0);
+    }else{
+        laShowLabel(uil, c, "LaGUI", 0, 0);
+        laShowLabel(uil, c, "A graphical user interface application toolkit", 0, 0)->Flags|=LA_TEXT_LINE_WRAP;
+        laShowLabel(uil, c, "(C)Yiming Wu", 0, 0);
+    }
 
     t = laMakeTab(uil, c, 0);{
         first = u = laAddTabPage(t, "Version");
         tc = laFirstColumn(u);
 
+        if(MAIN.AboutVersion){
+            MAIN.AboutVersion(u,Base,OperatorInst,ExtraColumns,0);
+        }
+
         g = laMakeGroup(u, tc, "LaGUI information", 0);
         gu = g->Page;{
             gc = laFirstColumn(gu);
@@ -1313,20 +1360,20 @@ void laui_About(laUiList *uil, laPropPack *Base, laPropPack *OperatorInst, laCol
             laShowLabel(gu, gc, buf, 0, 0)->Flags|=LA_TEXT_MONO;
             sprintf(buf, "Program built on:[UTC+8] %s %s", __DATE__, __TIME__);
             laShowLabel(gu, gc, buf, 0, 0)->Flags|=LA_TEXT_MONO;
-            sprintf(buf, "UDF Capability %s %d.%d.%d",LA_UDF_IDENTIFIER, LA_UDF_CAPABILITY_MAIN, LA_UDF_CAPABILITY_SUB, LA_UDF_CAPABILITY_MINOR);
+            sprintf(buf, "UDF Identifier: %s",LA_UDF_IDENTIFIER);
             laShowLabel(gu, gc, buf, 0, 0)->Flags|=LA_TEXT_MONO;
             laShowLabel(gu, gc, "UDF Extensions:", 0, 0)->Flags|=LA_TEXT_MONO;
-            laShowLabel(gu, gc, "🗸 LA_UDF_BASICS", 0, 0)->Flags|=LA_TEXT_MONO;
+            laShowLabel(gu, gc, "🗸LA_UDF_BASICS", 0, 0)->Flags|=LA_TEXT_MONO;
             for(int i=0;i<64;i++){
                 if(!LA_UDF_EXTENSION_STRINGS[i][0]) break; int gray=!((1<<i)&LA_UDF_EXTENSION_BITS);
-                sprintf(buf,"%s %s",gray?" ":"🗸",LA_UDF_EXTENSION_STRINGS[i]);
+                sprintf(buf,"%s%s",gray?"  ":"🗸",LA_UDF_EXTENSION_STRINGS[i]);
                 laUiItem* ext=laShowLabel(gu, gc, buf, 0, 0); ext->Flags|=LA_TEXT_MONO;
                 if(gray){ ext->Flags|=LA_UI_FLAGS_DISABLED; }
             }
 
         }
         
-        g = laMakeGroup(u, tc, "Graphics", 0);
+        g = laMakeGroup(u, tc, "Device Graphics", 0);
         gu = g->Page;{
             gc = laFirstColumn(gu);
             sprintf(buf, "OpenGL Version: %s", T->GLVersionStr);   laShowLabel(gu, gc, buf, 0, 0)->Flags|=LA_TEXT_MONO;
@@ -1335,64 +1382,22 @@ void laui_About(laUiList *uil, laPropPack *Base, laPropPack *OperatorInst, laCol
             sprintf(buf, "GLSL Version: %s", T->GLSLVersionStr);   laShowLabel(gu, gc, buf, 0, 0)->Flags|=LA_TEXT_MONO;
         }
 
-        u = laAddTabPage(t, "Props");{
-            c = laFirstColumn(u);
-            laSplitColumn(u, c, 0.2);
-            gcl = laLeftColumn(c, 1);
-            gcr = laRightColumn(c, 0);
-            for (pc = MAIN.PropContainers.pFirst; pc; pc = pc->Item.pNext){
-                buf[0] = L' ';buf[1]= L'\0';
-                if(pc->IconID){
-                    sprintf(buf, "%lc", pc->IconID);
-                }
-                laShowLabelDynamic(u, gcl, buf, 0, LA_WIDGET_STRING_PLAIN);
-                laShowLabelDynamic(u, gcr, pc->Identifier, 0, LA_WIDGET_STRING_PLAIN);
-            }
-        }
+        u = laAddTabPage(t, "Authors");
+        tc= laFirstColumn(u);
 
-        u = laAddTabPage(t, "Functions");{
-            c = laFirstColumn(u);
-            laSplitColumn(u, c, 0.2);
-            gcl = laLeftColumn(c, 1);
-            gcr = laRightColumn(c, 0);
-            for (h = 0; h < 256; h++){
-                for (at = MAIN.OperatorTypeHash.Entries[h].pFirst; at; at = at->Item.pNext){
-                    buf[0] = L' ';buf[1]= L'\0';
-                    if(at->IconID){
-                        sprintf(buf, "%lc", at->IconID);
-                    }
-                    laShowLabelDynamic(u, gcl, buf, 0, LA_WIDGET_STRING_PLAIN);
-                    laShowLabelDynamic(u, gcr, at->Identifier, 0, LA_WIDGET_STRING_PLAIN);
-                }
-            }
+        if(MAIN.AboutAuthor){
+            MAIN.AboutAuthor(u,Base,OperatorInst,ExtraColumns,0);
         }
 
-        u = laAddTabPage(t, "Author");
-        tc = laFirstColumn(u);
-        laSplitColumn(u, tc, 0.8);
-        tcl = laLeftColumn(tc, 3);
-        tcr = laRightColumn(tc, 00);
-
-        strSafeSet(&laShowSymbol(u, tc, 0, 0)->ExtraInstructions, "mode=invert;preserve=5;");
-
-        laShowItemFull(u, tcl, 0, "LA_open_internet_link", 0, "link=http://www.wellobserve.com;", 0, 0);
-        laShowLabel(u, tcr, "Yiming's Website", 0, 0);
-        laShowSeparator(u, tc);
-
-        laShowLabel(u, tcr, "QQ 1094469528", 0, 0);
-        laShowSeparator(u, tc);
-
-        laShowItemFull(u, tcl, 0, "LA_open_internet_link", 0, "link=http://weibo.com/1919404360;", 0, 0);
-        laShowLabel(u, tcr, "Weibo", 0, 0);
-
-        laShowItemFull(u, tcl, 0, "LA_open_internet_link", 0, "link=https://www.artstation.com/artist/nicksbest;", 0, 0);
-        laShowLabel(u, tcr, "Artstation", 0, 0);
-
-        laShowItemFull(u, tcl, 0, "LA_open_internet_link", 0, "link=mailto:1094469528@qq.com;icon=🖅", 0, 0);
-        laShowLabel(u, tcr, "QQ Mail", 0, 0);
-
-        laShowLabel(u, tcr, "Outlook Mail", 0, 0);
-        laShowItemFull(u, tcl, 0, "LA_open_internet_link", 0, "link=mailto:xp8110@outlook.com;icon=🖅", 0, 0);
+        g = laMakeGroup(u, tc, "LaGUI", 0);
+        gu = g->Page;{
+            gc = laFirstColumn(gu);
+            laShowLabel(gu,gc,"LaGUI application framework is made by Wu Yiming.",0,0)->Flags|=LA_TEXT_LINE_WRAP;
+            laUiItem* b=laBeginRow(gu,gc,1,0);
+            laShowItemFull(gu, gc, 0, "LA_open_internet_link", 0, "link=http://www.ChengduLittleA.com/lagui;text=Details", 0, 0);
+            laShowItemFull(gu, gc, 0, "LA_open_internet_link", 0, "link=http://www.ChengduLittleA.com;text=Yiming's Blog", 0, 0);
+            laEndRow(gu,b);
+        }
 
         t->Page = first;
     }

+ 2 - 3
resources/la_widgets.c

@@ -892,6 +892,7 @@ void la_SingleLineStringDraw(laUiItem *ui, int h){
     char* _temp[LA_RAW_CSTR_MAX_LEN] = {0}; char* temp=_temp;
     uint32_t* line=0;
     int NoDecal=ui->Flags&LA_UI_FLAGS_NO_DECAL;
+    int IsDisabled=ui->Flags&LA_UI_FLAGS_DISABLED;
 
     if (!ui->State) ui->State = LA_UI_NORMAL;
 
@@ -916,9 +917,7 @@ void la_SingleLineStringDraw(laUiItem *ui, int h){
 
     if (line) la_SingleLineStringDrawSelection(ui, ui->L+(NoDecal?0:bt->LM), ui->U, bt, line, ui->Extra->Edit);
 
-    tnsColor4dv(laThemeColor(bt, LA_BT_TEXT));
-
-    tnsDrawStringAutoM(line?0:temp, line, laThemeColor(bt, LA_BT_TEXT), ui->L+(NoDecal?0:bt->LM), ui->R-(NoDecal?0:bt->RM), ui->U, ui->Flags);
+    tnsDrawStringAutoM(line?0:temp, line, IsDisabled?laThemeColor(bt,LA_BT_TEXT|LA_UI_DISABLED):laThemeColor(bt,LA_BT_TEXT), ui->L+(NoDecal?0:bt->LM), ui->R-(NoDecal?0:bt->RM), ui->U, ui->Flags);
 }
 void la_MultiStringDraw(laUiItem *ui, int h){
     laBoxedTheme *bt = (*ui->Type->Theme);