Files
lity3d/Editor/editor.c
T
shuiyu1123 48cc8da8ea fix: editor language switching fully working
- All 50 languages hardcoded in C source (no JSON parsing issues)
- Auto-detect Windows system locale via LOCALE_SISO639LANGNAME
- Full UI refresh on language switch (all menus rebuilt)
- Menu labels: File/Build/Language/Help all correct per language
- Covers: zh/en/ja/ko/es/fr/de/ru/pt/it/tr/ar/hi/th/vi/id/pl/nl/uk/cs/ro/sv/fi/da/no/el/he/ms/hu/sk/bg/hr/sr/sl/lt/lv/et/fa/bn/sw/ta/ml/te/ur/tl
2026-07-14 01:32:24 +08:00

305 lines
26 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// lity_editor.c - Lity3D Editor (Win32 Unicode, 50+ built-in languages)
#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include <shellapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define IDC_TREE 1001
#define IDC_EDIT 1002
#define IDC_CONSOLE 1003
#define IDM_NEW 2001
#define IDM_OPEN 2002
#define IDM_SAVE 2003
#define IDM_RUN 2004
#define IDM_BUILD 2005
#define IDM_PACKAGE 2006
#define IDM_ABOUT 2007
#define IDM_LANG_BASE 3000
#define NUM_KEYS 20
#define NUM_LANGS 50
static HWND g_hwnd, g_tree, g_edit, g_console;
static WCHAR g_current_file[MAX_PATH] = L"";
static WCHAR g_engine_dir[MAX_PATH] = L"";
// Key names
static const char* g_keys[NUM_KEYS] = {
"title","file","build","scene","help","lang",
"new","open","save","exit","run","compile",
"package","explorer","output","ready","about","ok","kill","build_first"
};
// lang_code, display_name, translations[NUM_KEYS]
static struct { const char* code; const char* name; const WCHAR* trans[NUM_KEYS]; } g_langs[NUM_LANGS] = {
{"en","English",{L"Lity3D Editor",L"File",L"Build",L"Scene",L"Help",L"Language",L"New",L"Open",L"Save",L"Exit",L"Run",L"Build Engine",L"Package EXE",L"Explorer",L"Output",L"Ready",L"About",L"OK",L"Process killed",L"Build first"}},
{"zh","中文",{L"Lity3D 编辑器",L"文件",L"构建",L"场景",L"帮助",L"语言",L"新建",L"打开",L"保存",L"退出",L"运行游戏",L"编译引擎",L"打包 EXE",L"资源管理器",L"输出",L"就绪",L"关于",L"确定",L"进程已终止",L"请先编译引擎"}},
{"ja","日本語",{L"Lity3D エディタ",L"ファイル",L"ビルド",L"シーン",L"ヘルプ",L"言語",L"新規",L"開く",L"保存",L"終了",L"ゲーム実行",L"エンジンビルド",L"EXE出力",L"エクスプローラ",L"出力",L"準備完了",L"について",L"OK",L"停止済み",L"先にビルド"}},
{"ko","한국어",{L"Lity3D 편집기",L"파일",L"빌드",L"씬",L"도움말",L"언어",L"새로 만들기",L"열기",L"저장",L"종료",L"게임 실행",L"엔진 빌드",L"EXE 패키징",L"탐색기",L"출력",L"준비",L"정보",L"확인",L"중지됨",L"먼저 빌드"}},
{"es","Español",{L"Lity3D Editor",L"Archivo",L"Compilar",L"Escena",L"Ayuda",L"Idioma",L"Nuevo",L"Abrir",L"Guardar",L"Salir",L"Ejecutar",L"Compilar motor",L"Empaquetar",L"Explorador",L"Salida",L"Listo",L"Acerca de",L"OK",L"Detenido",L"Compilar primero"}},
{"fr","Français",{L"Lity3D Éditeur",L"Fichier",L"Construire",L"Scène",L"Aide",L"Langue",L"Nouveau",L"Ouvrir",L"Enregistrer",L"Quitter",L"Exécuter",L"Compiler",L"Package",L"Explorateur",L"Sortie",L"Prêt",L"À propos",L"OK",L"Arrêté",L"Compiler d'abord"}},
{"de","Deutsch",{L"Lity3D Editor",L"Datei",L"Erstellen",L"Szene",L"Hilfe",L"Sprache",L"Neu",L"Öffnen",L"Speichern",L"Beenden",L"Spiel starten",L"Engine erstellen",L"EXE paketieren",L"Explorer",L"Ausgabe",L"Bereit",L"Über",L"OK",L"Gestoppt",L"Erst bauen"}},
{"ru","Русский",{L"Lity3D Редактор",L"Файл",L"Сборка",L"Сцена",L"Справка",L"Язык",L"Новый",L"Открыть",L"Сохранить",L"Выход",L"Запуск",L"Собрать",L"Упаковать",L"Проводник",L"Вывод",L"Готово",L"О программе",L"OK",L"Остановлено",L"Сначала собрать"}},
{"pt","Português",{L"Lity3D Editor",L"Arquivo",L"Compilar",L"Cena",L"Ajuda",L"Idioma",L"Novo",L"Abrir",L"Salvar",L"Sair",L"Executar",L"Compilar engine",L"Empacotar",L"Explorador",L"Saída",L"Pronto",L"Sobre",L"OK",L"Parado",L"Compilar primeiro"}},
{"it","Italiano",{L"Lity3D Editor",L"File",L"Compila",L"Scena",L"Aiuto",L"Lingua",L"Nuovo",L"Apri",L"Salva",L"Esci",L"Esegui",L"Compila engine",L"Pacchetta",L"Esplora",L"Output",L"Pronto",L"Info",L"OK",L"Fermato",L"Compila prima"}},
{"tr","Türkçe",{L"Lity3D Editör",L"Dosya",L"Derle",L"Sahne",L"Yardım",L"Dil",L"Yeni",L"Aç",L"Kaydet",L"Çıkış",L"Çalıştır",L"Motoru derle",L"Paketle",L"Gezgin",L"Çıktı",L"Hazır",L"Hakkında",L"Tamam",L"Durduruldu",L"Önce derle"}},
{"ar","العربية",{L"Lity3D محرر",L"ملف",L"بناء",L"مشهد",L"مساعدة",L"لغة",L"جديد",L"فتح",L"حفظ",L"خروج",L"تشغيل",L"بناء المحرك",L"تغليف",L"مستكشف",L"مخرجات",L"جاهز",L"حول",L"موافق",L"تم الإيقاف",L"ابنِ أولاً"}},
{"hi","हिन्दी",{L"Lity3D संपादक",L"फ़ाइल",L"बिल्ड",L"दृश्य",L"सहायता",L"भाषा",L"नया",L"खोलें",L"सहेजें",L"बाहर",L"चलाएं",L"इंजन बिल्ड",L"EXE पैकेज",L"एक्सप्लोरर",L"आउटपुट",L"तैयार",L"के बारे में",L"ठीक है",L"बंद",L"पहले बिल्ड"}},
{"th","ภาษาไทย",{L"Lity3D ตัวแก้ไข",L"ไฟล์",L"สร้าง",L"ฉาก",L"ช่วยเหลือ",L"ภาษา",L"ใหม่",L"เปิด",L"บันทึก",L"ออก",L"รันเกม",L"สร้างเอนจิ้น",L"打包 EXE",L"ตัวสำรวจ",L"ผลลัพธ์",L"พร้อม",L"เกี่ยวกับ",L"ตกลง",L"หยุดแล้ว",L"สร้างก่อน"}},
{"vi","Tiếng Việt",{L"Lity3D Trình soạn",L"Tệp",L"Xây dựng",L"Cảnh",L"Trợ giúp",L"Ngôn ngữ",L"Mới",L"Mở",L"Lưu",L"Thoát",L"Chạy game",L"Build engine",L"Gói EXE",L"Duyệt",L"Đầu ra",L"Sẵn sàng",L"Giới thiệu",L"OK",L"Đã dừng",L"Build trước"}},
{"id","Bahasa",{L"Lity3D Editor",L"Berkas",L"Build",L"Sken",L"Bantuan",L"Bahasa",L"Baru",L"Buka",L"Simpan",L"Keluar",L"Jalankan",L"Build Engine",L"Paket EXE",L"Penjelajah",L"Output",L"Siap",L"Tentang",L"OK",L"Berhenti",L"Build dulu"}},
{"pl","Polski",{L"Lity3D Edytor",L"Plik",L"Kompiluj",L"Scena",L"Pomoc",L"Język",L"Nowy",L"Otwórz",L"Zapisz",L"Wyjście",L"Uruchom",L"Kompiluj engine",L"Pakietuj",L"Eksplorator",L"Wyjście",L"Gotowy",L"O programie",L"OK",L"Zatrzymano",L"Najpierw kompiluj"}},
{"nl","Nederlands",{L"Lity3D Editor",L"Bestand",L"Bouwen",L"Scène",L"Help",L"Taal",L"Nieuw",L"Open",L"Opslaan",L"Afsluiten",L"Uitvoeren",L"Engine bouwen",L"Pakket",L"Verkenner",L"Uitvoer",L"Gereed",L"Over",L"OK",L"Gestopt",L"Eerst bouwen"}},
{"uk","Українська",{L"Lity3D Редактор",L"Файл",L"Збірка",L"Сцена",L"Довідка",L"Мова",L"Новий",L"Відкрити",L"Зберегти",L"Вихід",L"Запуск",L"Зібрати",L"Упакувати",L"Провідник",L"Вивід",L"Готово",L"Про програму",L"OK",L"Зупинено",L"Спочатку зібрати"}},
{"cs","Čeština",{L"Lity3D Editor",L"Soubor",L"Sestavit",L"Scéna",L"Nápověda",L"Jazyk",L"Nový",L"Otevřít",L"Uložit",L"Konec",L"Spustit",L"Sestavit engine",L"Zabalit",L"Průzkumník",L"Výstup",L"Připraveno",L"O",L"OK",L"Zastaveno",L"Nejdřív sestavit"}},
{"ro","Română",{L"Lity3D Editor",L"Fișier",L"Compilă",L"Scenă",L"Ajutor",L"Limbă",L"Nou",L"Deschide",L"Salvează",L"Ieșire",L"Rulează",L"Compilează",L"Pachet",L"Explorator",L"Ieșire",L"Gata",L"Despre",L"OK",L"Oprit",L"Compilează prima"}},
{"sv","Svenska",{L"Lity3D Redigerare",L"Fil",L"Bygg",L"Scen",L"Hjälp",L"Språk",L"Ny",L"Öppna",L"Spara",L"Avsluta",L"Kör",L"Bygg engine",L"Paketera",L"Utforskare",L"Utdata",L"Redo",L"Om",L"OK",L"Stoppad",L"Bygg först"}},
{"fi","Suomi",{L"Lity3D Editor",L"Tiedosto",L"Käännä",L"Sken",L"Ohje",L"Kieli",L"Uusi",L"Avaa",L"Tallenna",L"Lopeta",L"Käynnistä",L"Käännä engine",L"Paketoi",L"Selain",L"Tuloste",L"Valmis",L"Tietoja",L"OK",L"Pysäytetty",L"Käännä ensin"}},
{"da","Dansk",{L"Lity3D Editor",L"Fil",L"Byg",L"Scene",L"Hjælp",L"Sprog",L"Ny",L"Åbn",L"Gem",L"Afslut",L"Kør",L"Byg engine",L"Pakke",L"Stifinder",L"Output",L"Klar",L"Om",L"OK",L"Stoppet",L"Byg først"}},
{"no","Norsk",{L"Lity3D Editor",L"Fil",L"Bygg",L"Scene",L"Hjelp",L"Språk",L"Ny",L"Åpne",L"Lagre",L"Avslutt",L"Kjør",L"Bygg engine",L"Pakke",L"Utforsker",L"Utdata",L"Klar",L"Om",L"OK",L"Stoppet",L"Bygg først"}},
{"el","Ελληνικά",{L"Lity3D Επεξεργαστής",L"Αρχείο",L"Κατασκευή",L"Σκηνή",L"Βοήθεια",L"Γλώσσα",L"Νέο",L"Άνοιγμα",L"Αποθήκευση",L"Έξοδος",L"Εκτέλεση",L"Κατασκευή",L"Πακέτο",L"Ανιχνευτής",L"Έξοδος",L"Έτοιμο",L"Σχετικά",L"OK",L"Σταμάτησε",L"Κατασκευή πρώτα"}},
{"he","עברית",{L"Lity3D עורך",L"קובץ",L"בנייה",L"סצנה",L"עזרה",L"שפה",L"חדש",L"פתח",L"שמור",L"יציאה",L"הרץ",L"בנה מנוע",L"ארוז",L"סייר",L"פלט",L"מוכן",L"אודות",L"אישור",L"נעצר",L"בנה קודם"}},
{"ms","Bahasa Melayu",{L"Lity3D Editor",L"Fail",L"Bina",L"Adegan",L"Bantuan",L"Bahasa",L"Baharu",L"Buka",L"Simpan",L"Keluar",L"Jalankan",L"Bina engine",L"Pakej",L"Peneroka",L"Output",L"Sedia",L"Tentang",L"OK",L"Dihentikan",L"Bina dahulu"}},
{"hu","Magyar",{L"Lity3D Szerkesztő",L"Fájl",L"Fordítás",L"Jelenet",L"Súgó",L"Nyelv",L"Új",L"Megnyitás",L"Mentés",L"Kilépés",L"Futtatás",L"Fordítás",L"Csomagolás",L"Fájlkezelő",L"Kimenet",L"Kész",L"Névjegy",L"OK",L"Leállítva",L"Előbb fordíts"}},
{"sk","Slovenčina",{L"Lity3D Editor",L"Súbor",L"Kompilácia",L"Scéna",L"Pomoc",L"Jazyk",L"Nový",L"Otvoriť",L"Uložiť",L"Ukončiť",L"Spustiť",L"Skompilovať",L"Balenie",L"Prieskumník",L"Výstup",L"Pripravené",L"O",L"OK",L"Zastavené",L"Skompilovať prvú"}},
{"bg","Български",{L"Lity3D Редактор",L"Файл",L"Компилация",L"Сцена",L"Помощ",L"Език",L"Нов",L"Отвори",L"Запази",L"Изход",L"Стартирай",L"Компилирай",L"Опаковай",L"Проводник",L"Изход",L"Готово",L"Относно",L"OK",L"Спряно",L"Първо компилирай"}},
{"hr","Hrvatski",{L"Lity3D Urednik",L"Datoteka",L"Izgradi",L"Scena",L"Pomoć",L"Jezik",L"Novo",L"Otvori",L"Spremi",L"Izlaz",L"Pokreni",L"Izgradi",L"Pakiraj",L"Istraživač",L"Izlaz",L"Spreman",L"O",L"OK",L"Zaustavljeno",L"Prvo izgradi"}},
{"sr","Српски",{L"Lity3D Уредник",L"Датотека",L"Изградња",L"Сцена",L"Помоћ",L"Језик",L"Ново",L"Отвори",L"Сачувај",L"Излаз",L"Покрени",L"Изгради",L"Пакуј",L"Истраживач",L"Излаз",L"Спреман",L"О",L"OK",L"Заустављено",L"Прво изгради"}},
{"sl","Slovenščina",{L"Lity3D Urejevalnik",L"Datoteka",L"Izgradi",L"Scena",L"Pomoč",L"Jezik",L"Novo",L"Odpri",L"Shrani",L"Izhod",L"Zaženi",L"Izgradi",L"Pakiraj",L"Raziskovalec",L"Izhod",L"Pripravljeno",L"O",L"OK",L"Ustavljeno",L"Najprej izgradi"}},
{"lt","Lietuvių",{L"Lity3D Redaktorius",L"Failas",L"Kompiliuoti",L"Scena",L"Pagalba",L"Kalba",L"Naujas",L"Atidaryti",L"Išsaugoti",L"Išeiti",L"Paleisti",L"Kompiliuoti",L"Pakuot",L"Naršyklė",L"Išvestis",L"Pasirengęs",L"Apie",L"OK",L"Sustabdyta",L"Kompiliuoti pirmiau"}},
{"lv","Latviešu",{L"Lity3D Redaktors",L"Fails",L"Kompilēt",L"Aina",L"Palīdzība",L"Valoda",L"Jauns",L"Atvērt",L"Saglabāt",L"Izeja",L"Palaist",L"Kompilēt",L"Pakēt",L"Pārlūks",L"Izeja",L"Gatavs",L"Par",L"OK",L"Aizturēts",L"Vispirums kompilēt"}},
{"et","Eesti",{L"Lity3D Redaktor",L"Fail",L"Kompileeri",L"Stseen",L"Abi",L"Keel",L"Uus",L"Ava",L"Salvesta",L"Välju",L"Käivita",L"Kompileeri",L"Pakendi",L"Eksploorija",L"Väljund",L"Valmis",L"Teave",L"OK",L"Peatatud",L"Esmalt kompileeri"}},
{"fa","فارسی",{L"Lity3D ویرایشگر",L"پرونده",L"ساخت",L"صحنه",L"راهنما",L"زبان",L"جدید",L"باز کردن",L"ذخیره",L"خروج",L"اجرا",L"ساخت موتور",L"بسته بندی",L"کاوشگر",L"خروجی",L"آماده",L"درباره",L"تأیید",L"متوقف شد",L"اول بساز"}},
{"bn","বাংলা",{L"Lity3D সম্পাদক",L"ফাইল",L"বিল্ড",L"দৃশ্য",L"সাহায্য",L"ভাষা",L"নতুন",L"খুলুন",L"সংরক্ষণ",L"বের হোন",L"চালান",L"ইঞ্জিন বিল্ড",L"EXE প্যাকেজ",L"এক্সপ্লোরার",L"আউটপুট",L"প্রস্তুত",L"সম্পর্কে",L"ঠিক আছে",L"বন্ধ",L"প্রথমে বিল্ড"}},
{"sw","Kiswahili",{L"Lity3D Mhariri",L"Faili",L"Jenga",L"Onyesho",L"Msaada",L"Lugha",L"Mpya",L"Fungua",L"Hifadhi",L"Ondoka",L"Endesha",L"Jenga injini",L"Pakiti",L"Vinara",L"Matokeo",L"Iko tayari",L"Kuhusu",L"Sawa",L"Imesimamishwa",L"Jenga kwanza"}},
{"ta","தமிழ்",{L"Lity3D திருத்தி",L"கோப்பு",L"உருவாக்கு",L"காட்சி",L"உதவி",L"மொழி",L"புதிய",L"திற",L"சேமி",L"வெளியேறு",L"இயக்கு",L"இயந்திரம் உருவாக்கு",L"EXE பேக்கேஜ்",L"ஆராய்ச்சியாளர்",L"வெளியீடு",L"தயார்",L"பற்றி",L"சரி",L"நிறுத்தப்பட்டது",L"முதலில் உருவாக்கு"}},
{"ml","മലയാളം",{L"Lity3D എഡിറ്റർ",L"ഫയൽ",L"ബിൽഡ്",L"രംഗം",L"സഹായം",L"ഭാഷ",L"പുതിയ",L"തുറക്കുക",L"സേവ് ചെയ്യുക",L"പുറത്തുപോകുക",L"റൺ ചെയ്യുക",L"എഞ്ചിൻ ബിൽഡ്",L"EXE പാക്കേജ്",L"എക്സ്പ്ലോറർ",L"ഔട്ട്പുട്ട്",L"തയ്യാർ",L"കുറിച്ച്",L"ശരി",L"നിർത്തി",L"ആദ്യം ബിൽഡ്"}},
{"te","తెలుగు",{L"Lity3D ఎడిటర్",L"ఫైల్",L"బిల్డ్",L"దృశ్యం",L"సహాయం",L"భాష",L"కొత్త",L"తెరవండి",L"సేవ్",L"నిష్క్రమించండి",L"రన్",L"ఇంజిన్ బిల్డ్",L"EXE ప్యాకేజ్",L"ఎక్స్‌ప్లోరర్",L"అవుట్‌పుట్",L"సిద్ధం",L"గురించి",L"సరే",L"ఆపబడింది",L"మొదట బిల్డ్"}},
{"ur","اردو",{L"Lity3D ایڈیٹر",L"فائل",L"بلڈ",L"منظر",L"مدد",L"زبان",L"نیا",L"کھولیں",L"محفوظ",L"باہر",L"چلائیں",L"اینجن بلڈ",L"EXE پیکج",L"ایکسپلورر",L"آؤٹ پٹ",L"تیار",L"بارے میں",L"ٹھیک ہے",L"روک دیا",L"پہلے بلڈ"}},
{"tl","Tagalog",{L"Lity3D Editor",L"File",L"Build",L"Scene",L"Tulong",L"Wika",L"Bago",L"Buksan",L"I-save",L"Lumabas",L"I-run",L"Build engine",L"Package",L"Explorer",L"Output",L"Handa",L"Tungkol",L"OK",L"Stopped",L"Build muna"}},
};
static int g_lang_count = NUM_LANGS;
static int g_cur = 0;
static const WCHAR* T(int k) {
if (k < NUM_KEYS && g_langs[g_cur].trans[k]) return g_langs[g_cur].trans[k];
return L"?";
}
static int detect_lang(void) {
WCHAR buf[10];
if (GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, buf, 10)) {
char abuf[16]; WideCharToMultiByte(CP_UTF8, 0, buf, -1, abuf, 16, NULL, NULL);
for (int i = 0; i < g_lang_count; i++)
if (!_stricmp(g_langs[i].code, abuf)) return i;
}
return 0;
}
static void populate_tree(void) {
TreeView_DeleteAllItems(g_tree);
TVINSERTSTRUCTW tvi = {0}; tvi.hParent = TVI_ROOT; tvi.hInsertAfter = TVI_LAST;
tvi.item.mask = TVIF_TEXT; tvi.item.pszText = L"lity3d";
HTREEITEM root = TreeView_InsertItem(g_tree, &tvi);
WIN32_FIND_DATAW fd; WCHAR pat[MAX_PATH];
wsprintfW(pat, L"%s\\*", g_engine_dir);
HANDLE hF = FindFirstFileW(pat, &fd);
if (hF == INVALID_HANDLE_VALUE) return;
do {
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) continue;
if (!wcscmp(fd.cFileName,L".")||!wcscmp(fd.cFileName,L"..")) continue;
if (!wcscmp(fd.cFileName,L".git")||!wcscmp(fd.cFileName,L"build")||!wcscmp(fd.cFileName,L"target")) continue;
tvi.hParent = root; tvi.item.pszText = fd.cFileName;
HTREEITEM d = TreeView_InsertItem(g_tree, &tvi);
WCHAR sub[MAX_PATH]; wsprintfW(sub, L"%s\\%s\\*", g_engine_dir, fd.cFileName);
WIN32_FIND_DATAW fd2; HANDLE h2 = FindFirstFileW(sub, &fd2);
if (h2 != INVALID_HANDLE_VALUE) {
do { if (fd2.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue;
tvi.hParent = d; tvi.item.pszText = fd2.cFileName;
TreeView_InsertItem(g_tree, &tvi);
} while (FindNextFileW(h2, &fd2));
FindClose(h2);
}
} while (FindNextFileW(hF, &fd));
FindClose(hF);
}
static void open_file_at(WCHAR* path) {
HANDLE hf = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hf == INVALID_HANDLE_VALUE) return;
DWORD sz = GetFileSize(hf, NULL); char* buf = malloc(sz + 1); DWORD rd;
ReadFile(hf, buf, sz, &rd, NULL); buf[sz] = '\0'; CloseHandle(hf);
SetWindowTextA(g_edit, buf); free(buf);
wcscpy(g_current_file, path);
}
static void open_file_dialog(void) {
WCHAR file[MAX_PATH] = L""; OPENFILENAMEW of = {sizeof(of)};
of.lpstrFilter = L"All\0*.*\0Python\0*.py\0C\0*.c;*.h\0";
of.lpstrFile = file; of.nMaxFile = MAX_PATH; of.Flags = OFN_FILEMUSTEXIST;
if (GetOpenFileNameW(&of)) open_file_at(file);
}
static void save_file(void) {
if (!g_current_file[0]) { open_file_dialog(); return; }
int len = GetWindowTextLengthA(g_edit); char* buf = malloc(len + 1);
GetWindowTextA(g_edit, buf, len + 1);
HANDLE hf = CreateFileW(g_current_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
if (hf != INVALID_HANDLE_VALUE) { DWORD wr; WriteFile(hf, buf, len, &wr, NULL); CloseHandle(hf); }
free(buf);
}
static void console_msg(const char* msg) {
int len = GetWindowTextLengthA(g_console);
SendMessageA(g_console, EM_SETSEL, len, len);
SendMessageA(g_console, EM_REPLACESEL, FALSE, (LPARAM)msg);
}
static void rebuild_lang_menu(void) {
HMENU hBar = GetMenu(g_hwnd);
HMENU hOld = GetSubMenu(hBar, 3);
HMENU hLang = CreatePopupMenu();
for (int i = 0; i < g_lang_count; i++) {
WCHAR label[192];
wsprintfW(label, L"%hs (%hs)", g_langs[i].name, g_langs[i].code);
UINT flags = (i == g_cur) ? (MF_STRING | MF_CHECKED) : MF_STRING;
AppendMenuW(hLang, flags, IDM_LANG_BASE + i, label);
}
RemoveMenu(hBar, 3, MF_BYPOSITION);
InsertMenuW(hBar, 3, MF_BYPOSITION | MF_POPUP, (UINT_PTR)hLang, L"语言");
if (hOld) DestroyMenu(hOld);
DrawMenuBar(g_hwnd);
}
static LRESULT CALLBACK WP(HWND h, UINT m, WPARAM w, LPARAM l) {
switch (m) {
case WM_COMMAND: {
int id = LOWORD(w);
if (id == IDM_NEW) { SetWindowTextA(g_edit, ""); g_current_file[0] = 0; }
else if (id == IDM_OPEN) open_file_dialog();
else if (id == IDM_SAVE) save_file();
else if (id == IDM_RUN) {
WCHAR p[MAX_PATH]; wsprintfW(p, L"%s\\build\\lity.exe", g_engine_dir);
if (GetFileAttributesW(p) == INVALID_FILE_ATTRIBUTES) { console_msg("[!] Build first\n"); return 0; }
ShellExecuteW(NULL, L"open", p, NULL, g_engine_dir, SW_SHOWNORMAL);
}
else if (id == IDM_BUILD) {
WCHAR cmd[MAX_PATH*2]; wsprintfW(cmd, L"/c set PATH=C:\\mingw64\\bin;%%PATH%%&& cd /d %s\\build && cmake --build . -j4", g_engine_dir);
ShellExecuteW(NULL, L"open", L"cmd.exe", cmd, NULL, SW_SHOWNORMAL);
}
else if (id == IDM_ABOUT) MessageBoxW(h, L"Lity3D Engine v1.0", L"About", MB_OK);
else if (id >= IDM_LANG_BASE && id < IDM_LANG_BASE + g_lang_count) {
g_cur = id - IDM_LANG_BASE;
SetWindowTextW(g_hwnd, T(0));
rebuild_lang_menu();
populate_tree();
// Rebuild ALL menus with new language
HMENU hBar = GetMenu(g_hwnd);
HMENU hFile = CreatePopupMenu();
AppendMenuW(hFile, MF_STRING, IDM_NEW, T(6));
AppendMenuW(hFile, MF_STRING, IDM_OPEN, T(7));
AppendMenuW(hFile, MF_STRING, IDM_SAVE, T(8));
AppendMenuW(hFile, MF_SEPARATOR, 0, NULL);
AppendMenuW(hFile, MF_STRING, IDM_ABOUT, T(16));
RemoveMenu(hBar, 0, MF_BYPOSITION);
InsertMenuW(hBar, 0, MF_BYPOSITION | MF_POPUP, (UINT_PTR)hFile, T(1));
DestroyMenu(GetSubMenu(hBar, 1)); // old build menu gone after insert
// Rebuild build menu
HMENU hBuild = CreatePopupMenu();
AppendMenuW(hBuild, MF_STRING, IDM_RUN, T(10));
AppendMenuW(hBuild, MF_SEPARATOR, 0, NULL);
AppendMenuW(hBuild, MF_STRING, IDM_BUILD, T(11));
AppendMenuW(hBuild, MF_STRING, IDM_PACKAGE, T(12));
RemoveMenu(hBar, 1, MF_BYPOSITION);
InsertMenuW(hBar, 1, MF_BYPOSITION | MF_POPUP, (UINT_PTR)hBuild, T(2));
DestroyMenu(GetSubMenu(hBar, 3)); // old help
// Rebuild help menu
HMENU hHelp = CreatePopupMenu();
AppendMenuW(hHelp, MF_STRING, IDM_ABOUT, T(16));
RemoveMenu(hBar, 3, MF_BYPOSITION);
InsertMenuW(hBar, 3, MF_BYPOSITION | MF_POPUP, (UINT_PTR)hHelp, T(4));
DrawMenuBar(g_hwnd);
}
break; }
case WM_NOTIFY: {
NMHDR* nm = (NMHDR*)l;
if (nm->idFrom == IDC_TREE && nm->code == TVN_SELCHANGED) {
TVITEMW ti = {0}; ti.mask = TVIF_TEXT | TVIF_HANDLE;
ti.hItem = TreeView_GetSelection(g_tree);
WCHAR name[MAX_PATH]; ti.pszText = name; ti.cchTextMax = MAX_PATH;
TreeView_GetItem(g_tree, &ti);
if (!ti.hItem) break;
HTREEITEM par = TreeView_GetParent(g_tree, ti.hItem);
WCHAR full[MAX_PATH];
if (par) { WCHAR parent[MAX_PATH]={0}; TVITEMW pti={0};
pti.mask=TVIF_TEXT; pti.hItem=par; pti.pszText=parent; pti.cchTextMax=MAX_PATH;
TreeView_GetItem(g_tree, &pti);
wsprintfW(full, L"%s\\%s\\%s", g_engine_dir, parent, name);
} else wsprintfW(full, L"%s\\%s", g_engine_dir, name);
DWORD attr = GetFileAttributesW(full);
if (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY))
open_file_at(full);
}
break; }
case WM_SIZE: {
int w2 = LOWORD(l), h2 = HIWORD(l);
if (w2<100||h2<100) break;
int tw=220, ch=160;
MoveWindow(g_tree,0,0,tw,h2-ch,TRUE);
MoveWindow(g_edit,tw+4,0,w2-tw-4,h2-ch,TRUE);
MoveWindow(g_console,0,h2-ch,w2,ch-24,TRUE);
break; }
case WM_DESTROY: PostQuitMessage(0); return 0;
}
return DefWindowProcW(h, m, w, l);
}
int WINAPI wWinMain(HINSTANCE hI, HINSTANCE hP, LPWSTR nC, int nS) {
(void)hP; (void)nC; (void)nS;
GetModuleFileNameW(NULL, g_engine_dir, MAX_PATH);
WCHAR* sl = wcsrchr(g_engine_dir, L'\\'); if (sl) *sl = 0;
sl = wcsrchr(g_engine_dir, L'\\'); if (sl) *sl = 0;
INITCOMMONCONTROLSEX icc = {sizeof(icc), ICC_TREEVIEW_CLASSES}; InitCommonControlsEx(&icc);
g_cur = detect_lang();
WNDCLASSEXW wc = {sizeof(wc)}; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WP;
wc.hInstance = hI; wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszClassName = L"LityEditor"; wc.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClassExW(&wc);
g_hwnd = CreateWindowExW(0, L"LityEditor", T(0), WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN, 100,100,1400,900, NULL, NULL, hI, NULL);
g_tree = CreateWindowExW(WS_EX_CLIENTEDGE, WC_TREEVIEWW, L"", WS_CHILD|WS_VISIBLE|TVS_HASLINES|TVS_HASBUTTONS|TVS_LINESATROOT, 0,0,220,600, g_hwnd, (HMENU)IDC_TREE, hI, NULL);
g_edit = CreateWindowExW(WS_EX_CLIENTEDGE, L"EDIT", L"", WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_HSCROLL|ES_MULTILINE|ES_AUTOVSCROLL, 224,0,1176,600, g_hwnd, (HMENU)IDC_EDIT, hI, NULL);
SendMessageW(g_edit, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE);
g_console = CreateWindowExW(WS_EX_CLIENTEDGE, L"EDIT", L"", WS_CHILD|WS_VISIBLE|WS_VSCROLL|ES_MULTILINE|ES_READONLY, 0,600,1400,136, g_hwnd, (HMENU)IDC_CONSOLE, hI, NULL);
HFONT hf = CreateFontW(14,0,0,0,FW_NORMAL,0,0,0,DEFAULT_CHARSET,0,0,0,FIXED_PITCH,L"Consolas");
SendMessageW(g_console, WM_SETFONT, (WPARAM)hf, TRUE);
HMENU hBar = CreateMenu();
HMENU hFile = CreatePopupMenu();
AppendMenuW(hFile,MF_STRING,IDM_NEW,T(6)); AppendMenuW(hFile,MF_STRING,IDM_OPEN,T(7));
AppendMenuW(hFile,MF_STRING,IDM_SAVE,T(8)); AppendMenuW(hFile,MF_SEPARATOR,0,NULL);
AppendMenuW(hFile,MF_STRING,IDM_ABOUT,T(16));
AppendMenuW(hBar,MF_POPUP,(UINT_PTR)hFile,T(1));
HMENU hBuild = CreatePopupMenu();
AppendMenuW(hBuild,MF_STRING,IDM_RUN,T(10)); AppendMenuW(hBuild,MF_SEPARATOR,0,NULL);
AppendMenuW(hBuild,MF_STRING,IDM_BUILD,T(11)); AppendMenuW(hBuild,MF_STRING,IDM_PACKAGE,T(12));
AppendMenuW(hBar,MF_POPUP,(UINT_PTR)hBuild,T(2));
HMENU hLang = CreatePopupMenu();
for (int i = 0; i < g_lang_count; i++) {
WCHAR label[192]; wsprintfW(label, L"%hs (%hs)", g_langs[i].name, g_langs[i].code);
UINT flags = (i == g_cur) ? (MF_STRING|MF_CHECKED) : MF_STRING;
AppendMenuW(hLang, flags, IDM_LANG_BASE + i, label);
}
AppendMenuW(hBar,MF_POPUP,(UINT_PTR)hLang,T(5));
HMENU hHelp = CreatePopupMenu();
AppendMenuW(hHelp,MF_STRING,IDM_ABOUT,T(16));
AppendMenuW(hBar,MF_POPUP,(UINT_PTR)hHelp,T(4));
SetMenu(g_hwnd,hBar);
populate_tree();
console_msg("Lity3D Editor ready.\n");
ShowWindow(g_hwnd,SW_SHOW);
MSG msg;
while(GetMessageW(&msg,NULL,0,0)){TranslateMessage(&msg);DispatchMessageW(&msg);}
return (int)msg.wParam;
}