From f77d3cd4dbf81e0ecada416ce70a7eba8243a2de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tur=C3=A1nszki=20J=C3=A1nos?= Date: Tue, 10 Jun 2025 06:39:07 +0200 Subject: [PATCH] added cursor editor to project creator (#1123) --- Editor/GeneralWindow.cpp | 1 + Editor/ProjectCreatorWindow.cpp | 107 ++++++++++++++++++++++++++++---- Editor/ProjectCreatorWindow.h | 16 +++-- WickedEngine/wiGUI.cpp | 7 ++- WickedEngine/wiGUI.h | 3 + WickedEngine/wiHelper.cpp | 4 +- WickedEngine/wiInput.h | 2 +- WickedEngine/wiVersion.cpp | 2 +- 8 files changed, 119 insertions(+), 23 deletions(-) diff --git a/Editor/GeneralWindow.cpp b/Editor/GeneralWindow.cpp index e4b24c41f..0f4f4a90f 100644 --- a/Editor/GeneralWindow.cpp +++ b/Editor/GeneralWindow.cpp @@ -520,6 +520,7 @@ void GeneralWindow::Create(EditorComponent* _editor) editor->projectCreatorWnd.iconButton.SetColor(wi::Color::White(), wi::gui::IDLE); editor->projectCreatorWnd.thumbnailButton.SetColor(wi::Color::White(), wi::gui::IDLE); editor->projectCreatorWnd.splashScreenButton.SetColor(wi::Color::White(), wi::gui::IDLE); + editor->projectCreatorWnd.cursorButton.SetColor(wi::Color::White(), wi::gui::IDLE); editor->aboutLabel.sprites[wi::gui::FOCUS] = editor->aboutLabel.sprites[wi::gui::IDLE]; int scene_id = 0; for (auto& editorscene : editor->scenes) diff --git a/Editor/ProjectCreatorWindow.cpp b/Editor/ProjectCreatorWindow.cpp index 6e006382e..edb123fff 100644 --- a/Editor/ProjectCreatorWindow.cpp +++ b/Editor/ProjectCreatorWindow.cpp @@ -35,7 +35,6 @@ void ProjectCreatorWindow::Create(EditorComponent* _editor) if (iconResource.IsValid()) { iconResource = {}; - iconName = {}; iconButton.SetImage(iconResource); } else @@ -50,7 +49,6 @@ void ProjectCreatorWindow::Create(EditorComponent* _editor) if (!res.IsValid()) return; iconResource.SetTexture(editor->CreateThumbnail(res.GetTexture(), 128, 128, true)); - iconName = fileName; iconButton.SetImage(iconResource); }); }); @@ -69,7 +67,6 @@ void ProjectCreatorWindow::Create(EditorComponent* _editor) if (thumbnailResource.IsValid()) { thumbnailResource = {}; - thumbnailName = {}; thumbnailButton.SetImage(thumbnailResource); } else @@ -84,7 +81,6 @@ void ProjectCreatorWindow::Create(EditorComponent* _editor) if (!res.IsValid()) return; thumbnailResource.SetTexture(editor->CreateThumbnail(res.GetTexture(), 480, 270)); - thumbnailName = fileName; thumbnailButton.SetImage(thumbnailResource); }); }); @@ -104,7 +100,6 @@ void ProjectCreatorWindow::Create(EditorComponent* _editor) { splashScreenResource = {}; splashScreenResourceCroppedPreview = {}; - splashScreenName = {}; splashScreenButton.SetImage(splashScreenResourceCroppedPreview); } else @@ -119,7 +114,6 @@ void ProjectCreatorWindow::Create(EditorComponent* _editor) if (!res.IsValid()) return; splashScreenResource = res; - splashScreenName = fileName; splashScreenResourceCroppedPreview.SetTexture(editor->CreateThumbnail(splashScreenResource.GetTexture(), 1280, 720, true)); splashScreenButton.SetImage(splashScreenResourceCroppedPreview); }); @@ -128,6 +122,50 @@ void ProjectCreatorWindow::Create(EditorComponent* _editor) }); AddWidget(&splashScreenButton); + cursorLabel.Create("projectCursorLabel"); + cursorLabel.SetText("You can add a custom cursor for your application here from an image. Specify the cursor click hotspot by clicking the image with the right mouse button and dragging the indicator."); + AddWidget(&cursorLabel); + + cursorButton.Create("projectCursorButton"); + cursorButton.SetText(""); + cursorButton.SetDescription("Cursor: "); + cursorButton.font_description.params.v_align = wi::font::WIFALIGN_BOTTOM; + cursorButton.font_description.params.h_align = wi::font::WIFALIGN_CENTER; + cursorButton.SetSize(XMFLOAT2(64, 64)); + cursorButton.SetTooltip("The cursor can be used as a custom cursor for your app."); + cursorButton.OnClick([this](wi::gui::EventArgs args) { + if (cursorResource.IsValid()) + { + cursorResource = {}; + cursorButton.SetImage(cursorResource); + } + else + { + wi::helper::FileDialogParams params; + params.type = wi::helper::FileDialogParams::OPEN; + params.description = "Texture"; + params.extensions = wi::resourcemanager::GetSupportedImageExtensions(); + wi::helper::FileDialog(params, [this](std::string fileName) { + wi::eventhandler::Subscribe_Once(wi::eventhandler::EVENT_THREAD_SAFE_POINT, [=](uint64_t userdata) { + wi::Resource res = wi::resourcemanager::Load(fileName); + if (!res.IsValid()) + return; + cursorResource.SetTexture(editor->CreateThumbnail(res.GetTexture(), 64, 64, true)); + cursorButton.SetImage(cursorResource); + }); + }); + } + }); + cursorButton.OnDrag([this](wi::gui::EventArgs args) { + if (wi::math::Length(args.deltaPos) > 0.1f) + { + hotspotX = saturate(inverse_lerp(cursorButton.GetPos().x, cursorButton.GetPos().x + cursorButton.GetSize().x, args.clickPos.x)); + hotspotY = saturate(inverse_lerp(cursorButton.GetPos().y, cursorButton.GetPos().y + cursorButton.GetSize().y, args.clickPos.y)); + cursorButton.DisableClickForCurrentDragOperation(); + } + }); + AddWidget(&cursorButton); + backlogColorPicker.Create("backlog color", wi::gui::Window::WindowControls::NONE); backlogColorPicker.SetSize(XMFLOAT2(256, 256)); backlogColorPicker.SetPickColor(exe_customization.backlog_color); @@ -140,7 +178,7 @@ void ProjectCreatorWindow::Create(EditorComponent* _editor) colorPreviewButton.Create("colorPreviewButton"); colorPreviewButton.SetText("Preview:\nThese colors will be used by the application when showing the initialization screen. The text color will be also used for the backlog.\n\n[Click on this preview to reset colors]"); - colorPreviewButton.SetSize(XMFLOAT2(256, 128)); + colorPreviewButton.SetSize(XMFLOAT2(256, 64)); colorPreviewButton.OnClick([this](wi::gui::EventArgs args) { backlogColorPicker.SetPickColor(exe_customization.backlog_color); backgroundColorPicker.SetPickColor(exe_customization.background_color); @@ -172,12 +210,22 @@ void ProjectCreatorWindow::Create(EditorComponent* _editor) wi::helper::DirectoryCreate(directory); wilog("Project creator: created directory %s", directory.c_str()); - static const std::string script = R"( + static const std::string script_header = R"( -- This script was generated by Wicked Editor Project Creator. -- Read the scripting API documentation here: https://github.com/turanszkij/WickedEngine/blob/master/Content/Documentation/ScriptingAPI-Documentation.md +)"; + static const std::string script_cursor = R"( +input.SetCursorFromFile(CURSOR_DEFAULT, script_dir() .. "cursor.cur") +)"; + + static const std::string script_process = R"( runProcess(function() + -- retrieve the global scene and camera objects that will be used for rendering + local scene = GetScene() + local camera = GetCamera() + -- set up a 3D render path, so if you load a model it will be displayed local renderpath = RenderPath3D() application.SetActivePath(renderpath, 1.0, 0, 0, 0, FadeType.CrossFade) -- 1 sec cross fade @@ -205,6 +253,15 @@ runProcess(function() end) )"; + std::string script = script_header; + + if (cursorResource.IsValid()) + { + script += script_cursor; + } + + script += script_process; + std::string scriptfilename = name + ".lua"; std::string scriptfilepath = directory + scriptfilename; if (!wi::helper::FileExists(scriptfilepath)) @@ -219,10 +276,6 @@ end) { wi::helper::saveTextureToFile(iconResource.GetTexture(), directory + "icon.png"); wi::helper::saveTextureToFile(iconResource.GetTexture(), directory + "icon.ico"); - - //wi::vector cursordata; - //wi::helper::CreateCursorFromTexture(iconResource.GetTexture(), 4, 4, cursordata); - //wi::helper::FileWrite(directory + "cursor.cur", cursordata.data(), cursordata.size()); } if (thumbnailResource.IsValid()) { @@ -232,6 +285,13 @@ end) { wi::helper::saveTextureToFile(splashScreenResource.GetTexture(), directory + "splash_screen.png"); } + if (cursorResource.IsValid()) + { + wi::graphics::Texture cursorTexture = cursorResource.GetTexture(); + wi::vector cursordata; + wi::helper::CreateCursorFromTexture(cursorTexture, int((float)cursorTexture.desc.width * hotspotX), int((float)cursorTexture.desc.height * hotspotY), cursordata); + wi::helper::FileWrite(directory + "cursor.cur", cursordata.data(), cursordata.size()); + } wi::unordered_set exes; exes.insert(wi::helper::BackslashToForwardSlash(wi::helper::GetExecutablePath())); @@ -377,6 +437,23 @@ end) SetVisible(false); } +void ProjectCreatorWindow::Render(const wi::Canvas& canvas, wi::graphics::CommandList cmd) const +{ + wi::gui::Window::Render(canvas, cmd); + + wi::gui::Window::ApplyScissor(canvas, scissorRect, cmd); + wi::image::Params fx; + fx.pos.x = cursorButton.GetPos().x + cursorButton.GetSize().x * hotspotX; + fx.pos.y = cursorButton.GetPos().y + cursorButton.GetSize().y * hotspotY; + fx.pivot = XMFLOAT2(0.5f, 0.5f); + fx.color = wi::Color::Black(); + fx.siz.x = 2; + fx.siz.y = 16; + wi::image::Draw(nullptr, fx, cmd); + fx.rotation = XM_PI * 0.5f; + wi::image::Draw(nullptr, fx, cmd); +} + void ProjectCreatorWindow::ResizeLayout() { wi::gui::Window::ResizeLayout(); @@ -392,6 +469,12 @@ void ProjectCreatorWindow::ResizeLayout() layout.jump(); + layout.add_right(cursorButton); + cursorLabel.SetPos(XMFLOAT2(layout.padding, cursorButton.GetPos().y)); + cursorLabel.SetSize(XMFLOAT2(layout.width - cursorButton.GetSize().x - layout.padding * 3, cursorButton.GetSize().y)); + + layout.jump(); + layout.add_fullwidth(colorPreviewButton); layout.add(backlogColorPicker, backgroundColorPicker); diff --git a/Editor/ProjectCreatorWindow.h b/Editor/ProjectCreatorWindow.h index 6672a759d..3979de7c5 100644 --- a/Editor/ProjectCreatorWindow.h +++ b/Editor/ProjectCreatorWindow.h @@ -13,22 +13,26 @@ public: wi::gui::Button iconButton; wi::gui::Button thumbnailButton; wi::gui::Button splashScreenButton; - wi::gui::Button createButton; + + wi::gui::Label cursorLabel; + wi::gui::Button cursorButton; wi::gui::ColorPicker backlogColorPicker; wi::gui::ColorPicker backgroundColorPicker; wi::gui::Button colorPreviewButton; + wi::gui::Button createButton; + wi::Resource iconResource; - std::string iconName; - wi::Resource thumbnailResource; - std::string thumbnailName; - wi::Resource splashScreenResource; wi::Resource splashScreenResourceCroppedPreview; - std::string splashScreenName; + wi::Resource cursorResource; + float hotspotX = 0.0f; + float hotspotY = 0.0f; + + void Render(const wi::Canvas& canvas, wi::graphics::CommandList cmd) const override; void ResizeLayout() override; }; diff --git a/WickedEngine/wiGUI.cpp b/WickedEngine/wiGUI.cpp index 28e6f6b98..a10fd1875 100644 --- a/WickedEngine/wiGUI.cpp +++ b/WickedEngine/wiGUI.cpp @@ -855,7 +855,7 @@ namespace wi::gui args.clickPos = pointerHitbox.pos; onDragEnd(args); - if (pointerHitbox.intersects(hitBox)) + if (pointerHitbox.intersects(hitBox) && !disableClicking) { // Click occurs when the button is released within the bounds onClick(args); @@ -979,6 +979,11 @@ namespace wi::gui font_description.params.posY = translation.y + scale.y * 0.5f; break; } + + if (state <= FOCUS) + { + disableClicking = false; + } } void Button::Render(const wi::Canvas& canvas, CommandList cmd) const { diff --git a/WickedEngine/wiGUI.h b/WickedEngine/wiGUI.h index b1b1a8ee2..ee3090e51 100644 --- a/WickedEngine/wiGUI.h +++ b/WickedEngine/wiGUI.h @@ -382,6 +382,7 @@ namespace wi::gui std::function onDragEnd; XMFLOAT2 dragStart = XMFLOAT2(0, 0); XMFLOAT2 prevPos = XMFLOAT2(0, 0); + bool disableClicking = false; public: void Create(const std::string& name); @@ -398,6 +399,8 @@ namespace wi::gui wi::SpriteFont font_description; void SetDescription(const std::string& desc) { font_description.SetText(desc); } const std::string GetDescription() const { return font_description.GetTextA(); } + + void DisableClickForCurrentDragOperation() { disableClicking = true; } }; // Generic scroll bar diff --git a/WickedEngine/wiHelper.cpp b/WickedEngine/wiHelper.cpp index f883471b6..e38ac3dba 100644 --- a/WickedEngine/wiHelper.cpp +++ b/WickedEngine/wiHelper.cpp @@ -956,8 +956,8 @@ namespace wi::helper for (uint16_t i = 0; i < icondir->idCount; ++i) { ico::ICONDIRENTRY* icondirentry = (ico::ICONDIRENTRY*)(data.data() + sizeof(ico::ICONDIR)) + i; - icondirentry->wPlanes = (uint16_t)hotspotX / (baseWidth / icondirentry->bWidth); - icondirentry->wBitCount = (uint16_t)hotspotY / (baseHeight / icondirentry->bHeight); + icondirentry->wPlanes = (uint16_t)hotspotX / (baseWidth / (icondirentry->bWidth == 0 ? 256 : icondirentry->bWidth)); + icondirentry->wBitCount = (uint16_t)hotspotY / (baseHeight / (icondirentry->bHeight == 0 ? 256 : icondirentry->bHeight)); } return true; diff --git a/WickedEngine/wiInput.h b/WickedEngine/wiInput.h index e97d6d437..39f127433 100644 --- a/WickedEngine/wiInput.h +++ b/WickedEngine/wiInput.h @@ -261,7 +261,7 @@ namespace wi::input void SetCursor(CURSOR cursor); // Replaces the specified cursor from a cursor image file - // (file type must be a .cur or .ani on Windows) + // (file type must be a .cur or .ani (.ani only works on Windows)) void SetCursorFromFile(CURSOR cursor, const char* filename); // Resets specified cursor to the original: diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index ef30d5121..1277e13a3 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 71; // minor bug fixes, alterations, refactors, updates - const int revision = 791; + const int revision = 792; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);