Introduced a Cython-based setup to compile Python files for performance improvements. Updated installer scripts with customtkinter modifications for better UI, added support for additional content loading, and enhanced project structure. Adjusted build commands, streamlined file paths, and integrated new UI elements in the HTML resources.
387 lines
12 KiB
C++
387 lines
12 KiB
C++
#include "toolboxWindow.h"
|
|
#include "optionWindow.h"
|
|
#include <iostream>
|
|
#include <windows.h>
|
|
#include <nlohmann/json.hpp>
|
|
#include <cppcodec/base64_rfc4648.hpp>
|
|
|
|
ToolboxWindow::ToolboxWindow(std::shared_ptr<saucer::application>& app)
|
|
: m_webview({.application = app}), m_app(app) {}
|
|
|
|
void ToolboxWindow::initialize() {
|
|
ensureToolboxInUserPath();
|
|
fillToolboxMenu();
|
|
configureWindow();
|
|
loadResources();
|
|
setupToolboxWindow();
|
|
}
|
|
|
|
void ToolboxWindow::setupToolboxWindow() {
|
|
SetJsonItems();
|
|
ExposeToJs();
|
|
m_webview.set_always_on_top(true);
|
|
m_webview.set_decorations(false);
|
|
|
|
#ifdef _DEBUG
|
|
m_webview.set_dev_tools(true);
|
|
#endif
|
|
|
|
startThreadMonitor();
|
|
}
|
|
|
|
void ToolboxWindow::startThreadMonitor() {
|
|
m_threadMonitor = std::thread([&]() {
|
|
monitor_focus();
|
|
});
|
|
m_threadMonitor.detach();
|
|
}
|
|
|
|
bool ToolboxWindow::serveHtmlFile(const std::string& htmlPath, bool setDecorations) {
|
|
auto resources = saucer::embedded::all();
|
|
|
|
if (!resources.contains(htmlPath)) {
|
|
std::cerr << "Fehler: '" << htmlPath << "' wurde nicht gefunden!" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
m_webview.serve(htmlPath);
|
|
m_webview.set_decorations(setDecorations);
|
|
return true;
|
|
}
|
|
|
|
void ToolboxWindow::configureWindow() {
|
|
const int screenWidth = GetSystemMetrics(SM_CXSCREEN);
|
|
const int screenHeight = GetSystemMetrics(SM_CYSCREEN);
|
|
|
|
const int windowWidth = static_cast<int>(screenWidth * (500.0 / 1920.0));
|
|
const int windowHeight = static_cast<int>(screenHeight * (700.0 / 1080.0));
|
|
|
|
m_webview.set_size(windowWidth, windowHeight);
|
|
m_webview.set_force_dark_mode(true);
|
|
|
|
const int xPos = screenWidth - windowWidth;
|
|
const int yPos = screenHeight - 42 - windowHeight;
|
|
|
|
if (const HWND hwnd = m_webview.module<hwnd_module>().get_hwnd()) {
|
|
SetWindowPos(hwnd, nullptr, xPos, yPos, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
|
|
}
|
|
}
|
|
|
|
void ToolboxWindow::loadResources() {
|
|
auto resources = saucer::embedded::all();
|
|
|
|
if (!resources.contains("html/card.html")) {
|
|
std::cerr << "Fehler: 'card.html' wurde nicht gefunden!" << std::endl;
|
|
return;
|
|
}
|
|
|
|
if (const auto it = resources.find("html/index.html"); it == resources.end()) {
|
|
std::cerr << "Fehler: 'index.html' wurde nicht gefunden!" << std::endl;
|
|
return;
|
|
}
|
|
m_webview.embed(resources);
|
|
m_webview.serve("html/index.html");
|
|
m_webview.set_context_menu(false);
|
|
|
|
if (!resources.contains("ico/toolbox.png")) {
|
|
std::cerr << "Fehler: 'toolbox.png' wurde nicht gefunden!" << std::endl;
|
|
return;
|
|
}
|
|
|
|
const saucer::icon window_icon = saucer::icon::from(resources.find("ico/toolbox.png")->second.content).value();
|
|
m_webview.set_icon(window_icon);
|
|
m_webview.set_title("Toolbox");
|
|
m_webview.register_scheme("Toolbox");
|
|
}
|
|
|
|
void ToolboxWindow::runLuaScript() {
|
|
sol::state lua;
|
|
lua.open_libraries(sol::lib::base);
|
|
lua.script("print('Hallo von Lua!')");
|
|
}
|
|
|
|
void ToolboxWindow::show() {
|
|
m_webview.show();
|
|
m_bShow = true;
|
|
}
|
|
|
|
void ToolboxWindow::hide() {
|
|
m_webview.hide();
|
|
m_bShow = false;
|
|
}
|
|
|
|
void ToolboxWindow::ExposeToJs() {
|
|
// Funktion, die JSON-Code an das JavaScript exponiert
|
|
m_webview.expose("getToolboxItems", [&]() -> std::string {
|
|
return jsonString;
|
|
}, saucer::launch::async);
|
|
|
|
m_webview.expose("run_lua", [&](int id) {
|
|
run_lua(id);
|
|
}, saucer::launch::async);
|
|
|
|
m_webview.expose("debug_print", [&]() {
|
|
debug_print();
|
|
}, saucer::launch::async);
|
|
|
|
m_webview.expose("open_file_system", [&]() {
|
|
open_file_system();
|
|
},saucer::launch::async);
|
|
|
|
m_webview.expose("openEinst", [&]() {
|
|
openEinst();
|
|
},saucer::launch::async);
|
|
|
|
m_webview.expose("openIndex", [&]() {
|
|
openIndex();
|
|
},saucer::launch::async);
|
|
|
|
m_webview.expose("getNewContent", [&]()
|
|
{
|
|
getNewContent();
|
|
},saucer::launch::async);
|
|
}
|
|
|
|
void ToolboxWindow::SetJsonItems() {// Konvertierung des Vektors in JSON
|
|
nlohmann::json jsonArray = nlohmann::json::array();
|
|
|
|
for (const auto& item : m_toolboxItems) {
|
|
nlohmann::json jsonItem = {
|
|
{"id", item.id},
|
|
{"name", item.name},
|
|
{"path", item.path},
|
|
{"icon", item.icon},
|
|
{"description", item.description},
|
|
{"category", item.category},
|
|
{"version", item.version},
|
|
{"author", item.author},
|
|
{"url", item.url},
|
|
{"license", item.license}
|
|
};
|
|
jsonArray.push_back(jsonItem);
|
|
}
|
|
jsonString = "";
|
|
// Konvertiere den JSON-Array in einen String
|
|
jsonString = jsonArray.dump();
|
|
}
|
|
|
|
bool ToolboxWindow::ensureToolboxInUserPath() {
|
|
// Bestimme das Benutzerverzeichnis abhängig vom Betriebssystem
|
|
std::filesystem::path userPath;
|
|
|
|
#ifdef _WIN32
|
|
userPath = std::getenv("USERPROFILE"); // Windows
|
|
#else
|
|
userPath = std::getenv("HOME"); // Unix/Linux/MacOS
|
|
#endif
|
|
|
|
// Setze den Pfad zum .Toolbox-Verzeichnis
|
|
|
|
// Überprüfe, ob der Pfad existiert
|
|
if (const std::filesystem::path toolboxPath = userPath / ".Toolbox"; !std::filesystem::exists(toolboxPath)) {
|
|
try {
|
|
// Versuche, das Verzeichnis zu erstellen
|
|
if (std::filesystem::create_directory(toolboxPath)) {
|
|
std::cout << ".Toolbox Verzeichnis wurde erstellt." << std::endl;
|
|
return true;
|
|
} else {
|
|
std::cerr << "Fehler beim Erstellen des .Toolbox Verzeichnisses." << std::endl;
|
|
return false;
|
|
}
|
|
} catch (const std::filesystem::filesystem_error& e) {
|
|
std::cerr << "Filesystem-Fehler: " << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::cout << "Das Verzeichnis .Toolbox existiert bereits." << std::endl;
|
|
return true;
|
|
}
|
|
|
|
void encodeImageToBase64(const std::filesystem::path& imagePath, ToolboxWindow::ToolboxItem& item) {
|
|
std::ifstream file(imagePath, std::ios::binary | std::ios::ate);
|
|
if (!file) {
|
|
std::cerr << "Fehler beim Öffnen des PNG-Bildes: " << imagePath << std::endl;
|
|
return;
|
|
}
|
|
|
|
const std::streamsize fileSize = file.tellg();
|
|
file.seekg(0, std::ios::beg);
|
|
|
|
std::vector<unsigned char> fileData(fileSize);
|
|
if (!file.read(reinterpret_cast<char*>(fileData.data()), fileSize)) {
|
|
std::cerr << "Fehler beim Lesen des PNG-Bildes: " << imagePath << std::endl;
|
|
return;
|
|
}
|
|
|
|
// Kodieren der Binärdaten mit cppcodec zu Base64
|
|
const std::string base64Encoded = cppcodec::base64_rfc4648::encode(fileData);
|
|
item.icon = "data:image/png;base64," + base64Encoded;
|
|
}
|
|
|
|
void ToolboxWindow::fillToolboxMenu() {
|
|
m_toolboxItems.clear();
|
|
std::filesystem::path toolboxDir = userPath / ".Toolbox";
|
|
|
|
if (!std::filesystem::exists(toolboxDir) || !std::filesystem::is_directory(toolboxDir)) {
|
|
std::cerr << "Fehler: Das Verzeichnis .Toolbox existiert nicht." << std::endl;
|
|
return;
|
|
}
|
|
|
|
int id = 0;
|
|
for (const auto& entry : std::filesystem::directory_iterator(toolboxDir)) {
|
|
if (!entry.is_directory()) continue;
|
|
|
|
ToolboxItem item;
|
|
item.id = std::to_string(id++);
|
|
item.name = entry.path().filename().string();
|
|
item.path = entry.path().string();
|
|
|
|
// Encode Image
|
|
encodeImageToBase64(entry.path() / "icon.png", item);
|
|
|
|
// Lese Beschreibung von description.txt
|
|
std::filesystem::path descriptionFile = entry.path() / "description.txt";
|
|
if (std::filesystem::exists(descriptionFile)) {
|
|
std::ifstream descFile(descriptionFile);
|
|
item.description.assign((std::istreambuf_iterator<char>(descFile)),
|
|
std::istreambuf_iterator<char>());
|
|
}
|
|
|
|
// Lese Beschreibung von description.txt
|
|
std::filesystem::path luaScript = entry.path() / "start.lua";
|
|
if (std::filesystem::exists(luaScript)) {
|
|
item.lua_script_path_string = luaScript.string();
|
|
}
|
|
|
|
m_toolboxItems.push_back(item);
|
|
}
|
|
}
|
|
|
|
void ToolboxWindow::run_lua(int id) {
|
|
|
|
/*// Öffnet ein CMD-Fenster, falls noch keins offen ist
|
|
static bool console_opened = false;
|
|
if (!console_opened) {
|
|
AllocConsole();
|
|
freopen("CONOUT$", "w", stdout); // stdout auf die Konsole umleiten
|
|
freopen("CONOUT$", "w", stderr); // stderr auf die Konsole umleiten
|
|
console_opened = true;
|
|
}*/
|
|
|
|
// Lambda-Funktion, um das Lua-Skript auszuführen
|
|
auto lua_task = [this, id]() {
|
|
sol::state lua;
|
|
lua.open_libraries(sol::lib::base,
|
|
sol::lib::os,
|
|
sol::lib::string,
|
|
sol::lib::coroutine,
|
|
sol::lib::package,
|
|
sol::lib::table,
|
|
sol::lib::math,
|
|
sol::lib::utf8,
|
|
sol::lib::io);
|
|
|
|
try {
|
|
std::string scriptPath = m_toolboxItems[id].lua_script_path_string;
|
|
std::cout << "Running Lua script: " << scriptPath << std::endl;
|
|
|
|
// Lua-Skript ausführen
|
|
lua.script_file(scriptPath);
|
|
|
|
// Nach erfolgreichem Ausführen UI aktualisieren
|
|
m_webview.execute("enableButtonByToolId({})", saucer::make_args(id));
|
|
m_webview.reload();
|
|
} catch (const sol::error& e) {
|
|
// Lua-Fehler in der Konsole ausgeben
|
|
std::cerr << "Lua error: " << e.what() << std::endl;
|
|
|
|
// Nach Fehler UI aktualisieren
|
|
m_webview.execute("enableButtonByToolId({})", saucer::make_args(id));
|
|
m_webview.reload();
|
|
}
|
|
};
|
|
|
|
// Erstellen und starten des Threads
|
|
std::thread lua_thread(lua_task);
|
|
|
|
// Thread lösen, um die Anwendung nicht zu blockieren
|
|
lua_thread.detach();
|
|
|
|
std::cout << "Lua-Skript wurde gestartet." << std::endl;
|
|
}
|
|
|
|
void ToolboxWindow::debug_print() {
|
|
std::cout << "ToolboxWindow::debug_print()" << std::endl;
|
|
}
|
|
|
|
ToolboxWindow::~ToolboxWindow() {
|
|
m_runningMonitor = false;
|
|
}
|
|
|
|
|
|
void ToolboxWindow::monitor_focus() {
|
|
while (m_runningMonitor) {
|
|
if (m_bShow && !m_webview.focused() && isIndex) {
|
|
this->hide();
|
|
}
|
|
|
|
// Schlafpause hinzufügen, um die CPU-Last zu reduzieren
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
}
|
|
}
|
|
|
|
void ToolboxWindow::open_file_system() {
|
|
if (const std::filesystem::path toolboxDir = userPath / ".Toolbox"; std::filesystem::exists(toolboxDir) && std::filesystem::is_directory(toolboxDir)) {
|
|
std::string path = toolboxDir.string();
|
|
std::string command;
|
|
|
|
#ifdef _WIN32
|
|
command = "explorer \"" + path + "\"";
|
|
#elif __APPLE__
|
|
command = "open \"" + path + "\"";
|
|
#elif __linux__
|
|
command = "xdg-open \"" + path + "\"";
|
|
#else
|
|
std::cerr << "Fehler: Plattform nicht unterstützt." << std::endl;
|
|
return;
|
|
#endif
|
|
|
|
if (const int result = std::system(command.c_str()); result != 0) {
|
|
std::cerr << "Fehler beim Öffnen des Verzeichnisses: " << result << std::endl;
|
|
}
|
|
} else {
|
|
std::cerr << "Fehler: Das Verzeichnis .Toolbox existiert nicht." << std::endl;
|
|
}
|
|
|
|
fillToolboxMenu();
|
|
SetJsonItems();
|
|
m_webview.execute("reloadContent({})",saucer::make_args());
|
|
}
|
|
|
|
|
|
// Beispielhafte Nutzung:
|
|
void ToolboxWindow::openEinst() {
|
|
if (!serveHtmlFile("html/einstellungen.html", true)) {
|
|
return;
|
|
}
|
|
isIndex = false;
|
|
}
|
|
|
|
void ToolboxWindow::openIndex() {
|
|
if (!serveHtmlFile("html/index.html", false)) {
|
|
return;
|
|
}
|
|
getNewContent();
|
|
}
|
|
|
|
void ToolboxWindow::getNewContent()
|
|
{
|
|
configureWindow();
|
|
fillToolboxMenu();
|
|
SetJsonItems();
|
|
m_webview.execute("reloadContent({})", saucer::make_args());
|
|
isIndex = true;
|
|
}
|
|
|