#include "stdafx.h" #include "ProjectCreatorWindow.h" #include "Utility/win32ico.h" using namespace wi::ecs; using namespace wi::scene; void ProjectCreatorWindow::Create(EditorComponent* _editor) { editor = _editor; control_size = 30; wi::gui::Window::Create("Project Creator"); infoLabel.Create("projectCreatorInfo"); infoLabel.SetFitTextEnabled(true); infoLabel.SetText("Here you can create a new Wicked Engine application project. It will create a new folder with the project name, and set up an executable, lua script startup, custom icon, thumbnail and base colors. Lua script will not be overwritten if it already exists in the destination folder with .lua."); AddWidget(&infoLabel); projectNameInput.Create("projectName"); projectNameInput.SetText(""); projectNameInput.SetDescription("Name: "); projectNameInput.SetCancelInputEnabled(false); AddWidget(&projectNameInput); reloadButton.Create(ICON_FA_ROTATE); reloadButton.SetTooltip("Reload last project"); reloadButton.SetSize(XMFLOAT2(reloadButton.GetSize().y, reloadButton.GetSize().y)); reloadButton.OnClick([this](wi::gui::EventArgs args) { projectNameInput.SetText(editor->main->config.GetSection("project").GetText("name")); iconFilename = editor->main->config.GetSection("project").GetText("icon"); thumbnailFilename = editor->main->config.GetSection("project").GetText("thumbnail"); splashScreenFilename = editor->main->config.GetSection("project").GetText("splash_screen"); cursorFilename = editor->main->config.GetSection("project").GetText("cursor"); hotspotX = editor->main->config.GetSection("project").GetFloat("cursor_hotspotX"); hotspotY = editor->main->config.GetSection("project").GetFloat("cursor_hotspotY"); fontColorPicker.SetPickColor(editor->main->config.GetSection("project").GetUint("font_color")); backgroundColorPicker.SetPickColor(editor->main->config.GetSection("project").GetUint("background_color")); if (iconFilename.empty()) { iconResource = {}; iconButton.SetImage(iconResource); } else { wi::Resource res = wi::resourcemanager::Load(iconFilename); if (!res.IsValid()) return; iconResource.SetTexture(editor->CreateThumbnail(res.GetTexture(), 128, 128, true)); iconButton.SetImage(iconResource); } if (thumbnailFilename.empty()) { thumbnailResource = {}; thumbnailButton.SetImage(thumbnailResource); } else { wi::Resource res = wi::resourcemanager::Load(thumbnailFilename); if (!res.IsValid()) return; thumbnailResource.SetTexture(editor->CreateThumbnail(res.GetTexture(), 480, 270, true)); thumbnailButton.SetImage(thumbnailResource); } if (splashScreenFilename.empty()) { splashScreenResource = {}; splashScreenButton.SetImage(splashScreenResource); } else { wi::Resource res = wi::resourcemanager::Load(splashScreenFilename); if (!res.IsValid()) return; splashScreenResource.SetTexture(editor->CreateThumbnail(res.GetTexture(), 1280, 720, true)); splashScreenButton.SetImage(splashScreenResource); } if (cursorFilename.empty()) { cursorResource = {}; cursorButton.SetImage(cursorResource); } else { wi::Resource res = wi::resourcemanager::Load(cursorFilename); if (!res.IsValid()) return; cursorResource.SetTexture(editor->CreateThumbnail(res.GetTexture(), 64, 64, true)); cursorButton.SetImage(cursorResource); } }); AddWidget(&reloadButton); iconButton.Create("projectIcon"); iconButton.SetText(""); iconButton.SetDescription("Icon: "); iconButton.SetSize(XMFLOAT2(128 * 0.5f, 128 * 0.5f)); iconButton.font_description.params.v_align = wi::font::WIFALIGN_BOTTOM; iconButton.font_description.params.h_align = wi::font::WIFALIGN_CENTER; iconButton.SetTooltip("The icon will be used for the executable icon."); iconButton.OnClick([this](wi::gui::EventArgs args) { if (iconResource.IsValid()) { iconResource = {}; iconButton.SetImage(iconResource); iconFilename = {}; } 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; iconResource.SetTexture(editor->CreateThumbnail(res.GetTexture(), 128, 128, true)); iconButton.SetImage(iconResource); iconFilename = fileName; }); }); } }); AddWidget(&iconButton); thumbnailButton.Create("projectThumbnail"); thumbnailButton.SetText(""); thumbnailButton.SetDescription("Thumbnail: "); thumbnailButton.font_description.params.v_align = wi::font::WIFALIGN_BOTTOM; thumbnailButton.font_description.params.h_align = wi::font::WIFALIGN_CENTER; thumbnailButton.SetSize(XMFLOAT2(480 * 0.4f, 270 * 0.4f)); thumbnailButton.SetTooltip("The thumbnail will be used for displaying the project in the editor."); thumbnailButton.OnClick([this](wi::gui::EventArgs args) { if (thumbnailResource.IsValid()) { thumbnailResource = {}; thumbnailButton.SetImage(thumbnailResource); thumbnailFilename = {}; } 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; thumbnailResource.SetTexture(editor->CreateThumbnail(res.GetTexture(), 480, 270)); thumbnailButton.SetImage(thumbnailResource); thumbnailFilename = fileName; }); }); } }); AddWidget(&thumbnailButton); splashScreenButton.Create("projectSplashScreen"); splashScreenButton.SetText(""); splashScreenButton.SetDescription("Splash screen: "); splashScreenButton.font_description.params.v_align = wi::font::WIFALIGN_BOTTOM; splashScreenButton.font_description.params.h_align = wi::font::WIFALIGN_CENTER; splashScreenButton.SetSize(XMFLOAT2(480 * 0.5f, 270 * 0.5f)); splashScreenButton.SetTooltip("The splash screen will be shown while the engine is initalizing.\nIf there is a splash screen, then it will replace the intialization log display."); splashScreenButton.OnClick([this](wi::gui::EventArgs args) { if (splashScreenResource.IsValid()) { splashScreenResource = {}; splashScreenResourceCroppedPreview = {}; splashScreenButton.SetImage(splashScreenResourceCroppedPreview); splashScreenFilename = {}; } 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; splashScreenResource = res; splashScreenResourceCroppedPreview.SetTexture(editor->CreateThumbnail(splashScreenResource.GetTexture(), 1280, 720, true)); splashScreenButton.SetImage(splashScreenResourceCroppedPreview); splashScreenFilename = fileName; }); }); } }); 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. Here you can load an image to create it."); cursorButton.OnClick([this](wi::gui::EventArgs args) { if (cursorResource.IsValid()) { cursorResource = {}; cursorButton.SetImage(cursorResource); cursorFilename = {}; } 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); cursorFilename = fileName; }); }); } }); 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); fontColorPicker.Create("font color", wi::gui::Window::WindowControls::NONE); fontColorPicker.SetSize(XMFLOAT2(256, 256)); fontColorPicker.SetPickColor(exe_customization.font_color); AddWidget(&fontColorPicker); backgroundColorPicker.Create("background color", wi::gui::Window::WindowControls::NONE); backgroundColorPicker.SetSize(XMFLOAT2(256, 256)); backgroundColorPicker.SetPickColor(exe_customization.background_color); AddWidget(&backgroundColorPicker); colorPreviewButton.Create("colorPreviewButton"); colorPreviewButton.SetText("Preview: these colors will be used by the application when showing the initialization screen and backlog text. [Click here to reset colors]"); colorPreviewButton.SetSize(XMFLOAT2(256, 64)); colorPreviewButton.OnClick([this](wi::gui::EventArgs args) { fontColorPicker.SetPickColor(exe_customization.font_color); backgroundColorPicker.SetPickColor(exe_customization.background_color); }); AddWidget(&colorPreviewButton); createButton.Create("projectCreate"); createButton.SetText(ICON_PROJECT_CREATE " Select folder and create project"); createButton.SetAngularHighlightWidth(6); createButton.SetSize(XMFLOAT2(64, 64)); createButton.font.params.scaling = 1.5f; createButton.OnClick([this](wi::gui::EventArgs args) { if (projectNameInput.GetText().empty()) { wi::helper::messageBox("Application name must be provided first!"); return; } std::string folder = wi::helper::FolderDialog("Select location where project folder will be created."); if (folder.empty()) return; std::string name = projectNameInput.GetText(); while (name.length() > 250) { // The name cannot be longer than 256 - ".lua", so cut from the end of it // It is specifically because I will later overwrite a reserved 256-long string inside the executable name.pop_back(); } std::string directory = folder + "/" + name + "/"; wi::helper::DirectoryCreate(directory); wilog("Project creator: created directory %s", directory.c_str()); static const std::string script_header = R"( -- This script was generated by Wicked Editor Project Creator, you can modify it to your needs. -- 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"( -- by running the script as a process, we can use engine events like update() in it to halt the script while the event doesn't arrive runProcess(function() -- retrieve the global scene and camera objects that will be used for 3D rendering local scene = GetScene() local camera = GetCamera() -- load a sample model simply into the current global scene from an asset file: local cube_root_entity = LoadModel(script_dir() .. "/cube.wiscene") -- create a point light to be able to see the cube: local light_entity = CreateEntity() local light = scene.Component_CreateLight(light_entity) light.SetType(POINT) light.SetIntensity(10) local light_transform = scene.Component_CreateTransform(light_entity) light_transform.Translate(Vector(2,2,-2)) -- put camera back a bit so we can see the cube in the origin (note that the camera is updated with this transform every frame when TransformCamera() is called): local cam_transform = TransformComponent() cam_transform.Translate(Vector(0, 2, -8)) -- 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 -- set up a simple 2D text that will dynamically change every frame local counter = 0 local font = SpriteFont() renderpath.AddFont(font) -- run an endless update loop, it will run until killProcesses() is called or the application exits while true do update() -- blocks this process until next update() is signaled from Wicked Engine local dt = getDeltaTime() -- get delta time (elapsed time since last update()) -- every frame the text is positioned to the upper center of the screen and display the value of the frame counter font.SetText("Hello World! Current frame counter = " .. counter .. "\nCamera look: right mouse button\nMove camera: WASD\nMove object: arrows\nIf you run this script from Wicked Editor, ESCAPE will return to the editor.") font.SetSize(24) -- the true render size of the font (larger can increase memory usage, but improves appearance) font.SetScale(2) -- upscaling the font without increasing the true font resolution font.SetPos(Vector(GetScreenWidth() * 0.5, GetScreenHeight() * 0.25)) -- put to upper center of the screen font.SetAlign(WIFALIGN_CENTER, WIFALIGN_CENTER) -- horizontal and vertical text align -- Mouse look camera: if input.Down(MOUSE_BUTTON_RIGHT) then local mouse_movement = input.GetPointerDelta() mouse_movement = vector.Multiply(mouse_movement, dt * 0.1) cam_transform.Rotate(Vector(mouse_movement.GetY(), mouse_movement.GetX())) -- roll-pitch-yaw rotation end -- WASD camera movement: local camspeed = 10 * dt local camera_movement = Vector() if input.Down(string.byte('W')) then camera_movement = vector.Add(camera_movement, Vector(0,0,camspeed)) end if input.Down(string.byte('S')) then camera_movement = vector.Add(camera_movement, Vector(0,0,-camspeed)) end if input.Down(string.byte('A')) then camera_movement = vector.Add(camera_movement, Vector(-camspeed,0)) end if input.Down(string.byte('D')) then camera_movement = vector.Add(camera_movement, Vector(camspeed,0)) end camera_movement = vector.Rotate(camera_movement, cam_transform.Rotation_local) -- rotate the camera movement with camera orientation, so it's relative cam_transform.Translate(camera_movement) cam_transform.UpdateTransform() -- because cam_transform is not part of the scene system, but we created it just in the script, update it manually with UpdateTransform() camera.TransformCamera(cam_transform) camera.UpdateCamera() -- rotate the cube every frame by a bit with the amount of delta time since last frame: local cube_transform = scene.Component_GetTransform(cube_root_entity) cube_transform.Rotate(Vector(0, dt * math.pi, 0)) -- arrows object movement: local movspeed = 10 * dt local object_movement = Vector() if input.Down(KEYBOARD_BUTTON_UP) then object_movement = vector.Add(object_movement, Vector(0,movspeed)) end if input.Down(KEYBOARD_BUTTON_DOWN) then object_movement = vector.Add(object_movement, Vector(0,-movspeed)) end if input.Down(KEYBOARD_BUTTON_LEFT) then object_movement = vector.Add(object_movement, Vector(-movspeed,0)) end if input.Down(KEYBOARD_BUTTON_RIGHT) then object_movement = vector.Add(object_movement, Vector(movspeed,0)) end object_movement = vector.Rotate(object_movement, cam_transform.Rotation_local) -- rotate the object movement with camera orientation, so it's relative cube_transform.Translate(object_movement) -- Add some editor testing functionality to return to editor when ESC is pressed. This can help development, and only works if script is running from the Editor: if IsThisEditor() and input.Press(KEYBOARD_BUTTON_ESCAPE) then ReturnToEditor() input.ResetCursor(CURSOR_DEFAULT) return end counter = counter + 1 end 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)) { wi::helper::FileWrite(scriptfilepath, (const uint8_t*)script.data(), script.size()); } else { wilog("Project creator: %s script file already exists, so it will not be overwritten.", scriptfilepath.c_str()); } if (iconResource.IsValid()) { wi::helper::saveTextureToFile(iconResource.GetTexture(), directory + "icon.png"); wi::helper::saveTextureToFile(iconResource.GetTexture(), directory + "icon.ico"); } if (thumbnailResource.IsValid()) { wi::helper::saveTextureToFile(thumbnailResource.GetTexture(), directory + "thumbnail.png"); } if (splashScreenResource.IsValid()) { 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()); } // Create a sample cube model for the project: { wi::scene::Scene samplescene; samplescene.Entity_CreateCube("cube"); wi::Archive samplescene_archive; samplescene.Serialize(samplescene_archive); samplescene_archive.SaveFile(directory + "cube.wiscene"); } if (wi::renderer::GetShaderDumpCount() == 0) { // If not using shader dump, try to copy shader compiler dlls into the project: std::string dxcompiler_dll_path = wi::helper::GetDirectoryFromPath(wi::helper::GetExecutablePath()) + "dxcompiler.dll"; std::string dxcompiler_so_path = wi::helper::GetDirectoryFromPath(wi::helper::GetExecutablePath()) + "libdxcompiler.so"; if (wi::helper::FileExists(dxcompiler_dll_path)) { wi::helper::FileCopy(dxcompiler_dll_path, directory + "dxcompiler.dll"); } if (wi::helper::FileExists(dxcompiler_so_path)) { wi::helper::FileCopy(dxcompiler_so_path, directory + "libdxcompiler.so"); } } wi::unordered_set exes; exes.insert(wi::helper::BackslashToForwardSlash(wi::helper::GetExecutablePath())); exes.insert(wi::helper::BackslashToForwardSlash(wi::helper::GetCurrentPath() + "/Editor_Windows.exe")); exes.insert(wi::helper::BackslashToForwardSlash(wi::helper::GetCurrentPath() + "/Editor_Linux")); for (auto& exepath_src : exes) { if (!wi::helper::FileExists(exepath_src)) continue; std::string exepath_dst = directory + name; std::string extension = wi::helper::toUpper(wi::helper::GetExtensionFromFileName(exepath_src)); if (extension.find("EXE") != std::string::npos) { exepath_dst += "_Windows.exe"; } else { exepath_dst += "_Linux"; } if (wi::helper::FileCopy(exepath_src, exepath_dst)) { wilog("Project creator: preparing executable %s -> %s", exepath_src.c_str(), exepath_dst.c_str()); wi::vector exedata; if (wi::helper::FileRead(exepath_dst, exedata)) { // ApplicationExeCustomization replacement in the executable: { wi::vector match_padded(arraysize(exe_customization.name_padded)); std::memcpy(match_padded.data(), exe_customization.name_padded, sizeof(exe_customization.name_padded)); auto it = std::search(exedata.begin(), exedata.end(), match_padded.begin(), match_padded.end()); if (it != exedata.end()) { wi::vector replacement(sizeof(ApplicationExeCustomization)); ApplicationExeCustomization& customization = *(ApplicationExeCustomization*)replacement.data(); std::memcpy(customization.name_padded, name.c_str(), name.length()); customization.font_color = fontColorPicker.GetPickColor(); customization.background_color = backgroundColorPicker.GetPickColor(); std::copy(replacement.begin(), replacement.end(), it); wilog("\tOverwritten ApplicationExeCustomization structure"); } } // startup script string replacement in the executable: { auto it = std::search(exedata.begin(), exedata.end(), editor->main->rewriteable_startup_script_text.begin(), editor->main->rewriteable_startup_script_text.end()); if (it != exedata.end()) { wi::vector replacement(scriptfilename.length() + 1); std::copy(scriptfilename.begin(), scriptfilename.end(), replacement.begin()); std::copy(replacement.begin(), replacement.end(), it); wilog("\tOverwritten startup script name: %s", scriptfilename.c_str()); } } // Win32 icon replacement: if (iconResource.IsValid()) { wi::graphics::Texture tex = iconResource.GetTexture(); const int resolutions[] = {128,64,32}; for (int res : resolutions) { const uint32_t pixelCount = res * res; const uint32_t rgbDataSize = pixelCount * 4; // 32-bit RGBA const uint32_t maskSize = ((res + 7) / 8) * res; // 1-bit mask, padded to byte const uint32_t bmpInfoHeaderSize = sizeof(ico::BITMAPINFOHEADER); ico::BITMAPINFOHEADER bmpHeader = { bmpInfoHeaderSize, // Size of header int32_t(res), // Width int32_t(res * 2), // Height (doubled for XOR + AND mask) 1, // Planes 32, // Bits per pixel 0, // No compression rgbDataSize + maskSize, // Image size 0, // X pixels per meter 0, // Y pixels per meter 0, // Colors used 0 // Important colors }; wi::vector bmpvec(sizeof(bmpHeader)); std::memcpy(bmpvec.data(), &bmpHeader, sizeof(bmpHeader)); // searches for exact BMP header match: // TODO: add some validation here because this method is quite stupid, just checking header bit pattern is not foolproof auto it = std::search(exedata.begin(), exedata.end(), bmpvec.begin(), bmpvec.end()); if (it != exedata.end()) { wi::vector iconfiledata; if (wi::helper::saveTextureToMemoryFile(editor->CreateThumbnail(tex, res, res, false), "ico", iconfiledata)) // note: individual mip thumbnails at this point! { // replace the BMP header and data part: std::copy(iconfiledata.begin() + sizeof(ico::ICONDIR) + sizeof(ico::ICONDIRENTRY), iconfiledata.end(), it); wilog("\tOverwritten Win32 icon at %d * %d resolution", res, res); } } } } // SDL icon replacement: if (iconResource.IsValid()) { wi::graphics::Texture tex = iconResource.GetTexture(); std::string match = "Wicked Editor Embedded Icon Data SDL"; wi::vector match_padded256(256); std::memcpy(match_padded256.data(), match.c_str(), match.length()); // searches for match string: auto it = std::search(exedata.begin(), exedata.end(), match_padded256.begin(), match_padded256.end()); if (it != exedata.end()) { wi::vector iconfiledata; if (wi::helper::saveTextureToMemoryFile(tex, "raw", iconfiledata)) { // replace the pixel data part: std::copy(iconfiledata.begin(), iconfiledata.end(), it + 256); wilog("\tOverwritten SDL icon"); } } } if (wi::helper::FileWrite(exepath_dst, exedata.data(), exedata.size())) { wilog("\tSuccessfully prepared executable %s", exepath_dst.c_str()); } else { wilog_error("\nWriting executable %s failed!", exepath_dst.c_str()); } } } } editor->main->config.GetSection("project").Set("name", name); editor->main->config.GetSection("project").Set("icon", iconFilename); editor->main->config.GetSection("project").Set("thumbnail", thumbnailFilename); editor->main->config.GetSection("project").Set("splash_screen", splashScreenFilename); editor->main->config.GetSection("project").Set("cursor", cursorFilename); editor->main->config.GetSection("project").Set("cursor_hotspotX", hotspotX); editor->main->config.GetSection("project").Set("cursor_hotspotY", hotspotY); editor->main->config.GetSection("project").Set("font_color", fontColorPicker.GetPickColor().rgba); editor->main->config.GetSection("project").Set("background_color", backgroundColorPicker.GetPickColor().rgba); editor->main->config.Commit(); editor->RegisterRecentlyUsed(directory + scriptfilename); wi::helper::OpenUrl(directory); }); AddWidget(&createButton); SetVisible(false); } void ProjectCreatorWindow::Render(const wi::Canvas& canvas, wi::graphics::CommandList cmd) const { wi::gui::Window::Render(canvas, cmd); if (!IsVisible()) return; 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::White(); fx.siz.x = 2; fx.siz.y = 16; fx.blendFlag = wi::enums::BLENDMODE_INVERSE; 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(); layout.margin_left = 60; layout.add_fullwidth(infoLabel); layout.add(projectNameInput); projectNameInput.SetSize(XMFLOAT2(projectNameInput.GetSize().x - reloadButton.GetSize().x - layout.padding * 2, projectNameInput.GetSize().y)); layout.y -= reloadButton.GetSize().y + reloadButton.GetShadowRadius() * 2 + layout.padding; layout.add_right(reloadButton); layout.jump(); layout.add_right(iconButton, thumbnailButton, splashScreenButton); 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(fontColorPicker, backgroundColorPicker); layout.jump(); layout.add_fullwidth(createButton); createButton.SetSize(XMFLOAT2(createButton.GetSize().x, std::max(30.0f, layout.height - createButton.GetPos().y - layout.padding * 2))); colorPreviewButton.SetColor(backgroundColorPicker.GetPickColor()); colorPreviewButton.font.params.color = fontColorPicker.GetPickColor(); colorPreviewButton.font.params.v_align = wi::font::WIFALIGN_TOP; colorPreviewButton.font.params.h_align = wi::font::WIFALIGN_LEFT; colorPreviewButton.font.params.h_wrap = colorPreviewButton.GetSize().x; }