Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified package/Interface/CommunityShaders/Icons/Action Icons/undo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,11 @@ struct BSInputDeviceManager_PollInputDevices
}

if (blockedDevice && menu->ShouldSwallowInput()) { //the menu is open, eat all keypresses
// During active flying preview, let input reach the game for movement/camera
if (menu->IsPreviewFlying()) {
func(a_dispatcher, a_events);
return;
}
constexpr RE::InputEvent* const dummy[] = { nullptr };
func(a_dispatcher, dummy);
return;
Expand Down
40 changes: 36 additions & 4 deletions src/Menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ Menu::~Menu()
uiIcons.debug.Release();
uiIcons.materials.Release();
uiIcons.postProcessing.Release();
uiIcons.freeCamera.Release();
uiIcons.playMode.Release();
uiIcons.search.Release();

// Clean up blur resources
BackgroundBlur::Cleanup();
Expand Down Expand Up @@ -949,9 +952,15 @@ void Menu::ProcessInputEventQueue()
}
if (event.device == RE::INPUT_DEVICE::kMouse) {
logger::trace("Detect mouse scan code {} value {} pressed: {}", event.keyCode, event.value, event.IsPressed());
auto* ew = EditorWindow::GetSingleton();
bool flying = ew && ew->IsPreviewFlying();
if (event.keyCode > 7) { // middle scroll
io.AddMouseWheelEvent(0, event.value * (event.keyCode == 8 ? 1 : -1));
} else {
if (ew && ew->previewMode == EditorWindow::PreviewMode::FreeCamera) {
ew->AdjustFlySpeed(event.keyCode == 8 ? 1.0f : -1.0f);
} else if (!flying) {
io.AddMouseWheelEvent(0, event.value * (event.keyCode == 8 ? 1 : -1));
}
} else if (!flying) {
Comment thread
Dlizzio marked this conversation as resolved.
if (event.keyCode > 5)
event.keyCode = 5;
io.AddMouseButtonEvent(event.keyCode, event.IsPressed());
Expand Down Expand Up @@ -1043,7 +1052,22 @@ void Menu::ProcessInputEventQueue()
{ settings.ShaderBlockPrevKey, [this, shaderCache]() { if (settings.EnableShaderBlocking) shaderCache->IterateShaderBlock(); } },
{ settings.ShaderBlockNextKey, [this, shaderCache]() { if (settings.EnableShaderBlocking) shaderCache->IterateShaderBlock(false); } },
{ settings.OverlayToggleKey, []() { Menu::GetSingleton()->overlayVisible = !Menu::GetSingleton()->overlayVisible; } },
{ settings.WeatherEditorToggleKey, []() { auto p = RE::PlayerCharacter::GetSingleton(); if (p && p->parentCell) EditorWindow::GetSingleton()->open = !EditorWindow::GetSingleton()->open; } },
{ settings.WeatherEditorToggleKey, []() {
auto* ew = EditorWindow::GetSingleton();
if (!ew)
return;
if (ew->GetPreviewMode() == EditorWindow::PreviewMode::FreeCamera) {
// Flying → lock camera position for editing
ew->ToggleFreeCameraLock();
} else if (ew->IsInPreviewMode()) {
// Locked or PlayMode → fully exit preview
ew->ExitPreviewMode();
} else {
auto p = RE::PlayerCharacter::GetSingleton();
if (p && p->parentCell)
ew->open = !ew->open;
}
} },
Comment thread
coderabbitai[bot] marked this conversation as resolved.
};
for (const auto& ka : keyActions) {
// Check if key matches last key in combo and all modifiers are held (exact match)
Expand Down Expand Up @@ -1083,7 +1107,9 @@ void Menu::ProcessInputEventQueue()
// Handle ESC key for menu and editor window
auto* editorWindow = EditorWindow::GetSingleton();
if (key == VK_ESCAPE) {
if (editorWindow && editorWindow->open && editorWindow->ShouldHandleEscapeKey()) {
if (editorWindow && editorWindow->IsInPreviewMode()) {
editorWindow->ExitPreviewMode();
} else if (editorWindow && editorWindow->open && editorWindow->ShouldHandleEscapeKey()) {
editorWindow->open = false;
} else if (IsEnabled && (!editorWindow || !editorWindow->open)) {
IsEnabled = false;
Expand Down Expand Up @@ -1154,6 +1180,12 @@ bool Menu::ShouldSwallowInput()
return IsEnabled || HomePageRenderer::ShouldShowFirstTimeSetup() || (editorWindow && editorWindow->open);
}

bool Menu::IsPreviewFlying()
{
auto editorWindow = EditorWindow::GetSingleton();
return editorWindow && editorWindow->IsPreviewFlying();
}

void Menu::SelectFeatureMenu(const std::string& featureName)
{
pendingFeatureSelection = featureName;
Expand Down
3 changes: 3 additions & 0 deletions src/Menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class Menu

void ProcessInputEvents(RE::InputEvent* const* a_events);
bool ShouldSwallowInput();
bool IsPreviewFlying();
std::string BuildFontSignature(float baseFontSize) const;

public:
Expand Down Expand Up @@ -200,6 +201,8 @@ class Menu
UIIcon applyToGame; // Apply changes to game icon (weather editor)
UIIcon pauseTime; // Pause time icon (weather editor)
UIIcon undo; // Undo icon (weather editor)
UIIcon freeCamera; // Free camera preview icon (weather editor)
UIIcon playMode; // Play mode preview icon (weather editor)

// Social media/external link icons
UIIcon discord;
Expand Down
37 changes: 15 additions & 22 deletions src/Menu/IconLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ namespace Util::IconLoader
{ std::string(iconFolder) + "\\apply-to-game.png", &menu->uiIcons.applyToGame.texture, &menu->uiIcons.applyToGame.size },
{ std::string(iconFolder) + "\\pause.png", &menu->uiIcons.pauseTime.texture, &menu->uiIcons.pauseTime.size },
{ std::string(iconFolder) + "\\undo.png", &menu->uiIcons.undo.texture, &menu->uiIcons.undo.size },
{ std::string(iconFolder) + "\\free-camera.png", &menu->uiIcons.freeCamera.texture, &menu->uiIcons.freeCamera.size },
{ std::string(iconFolder) + "\\play-mode.png", &menu->uiIcons.playMode.texture, &menu->uiIcons.playMode.size },
Comment thread
Dlizzio marked this conversation as resolved.

{ "Categories\\characters.png", &menu->uiIcons.characters.texture, &menu->uiIcons.characters.size },
{ "Categories\\display.png", &menu->uiIcons.display.texture, &menu->uiIcons.display.size },
Expand Down Expand Up @@ -194,20 +196,18 @@ namespace Util::IconLoader

auto iconDefs = GetIconDefinitions(menu);

for (auto* texturePtr : { &menu->uiIcons.saveSettings.texture, &menu->uiIcons.loadSettings.texture,
&menu->uiIcons.clearCache.texture, &menu->uiIcons.deleteSettings.texture, &menu->uiIcons.logo.texture,
&menu->uiIcons.featureSettingRevert.texture, &menu->uiIcons.applyToGame.texture, &menu->uiIcons.pauseTime.texture,
&menu->uiIcons.undo.texture, &menu->uiIcons.search.texture, &menu->uiIcons.discord.texture,
&menu->uiIcons.characters.texture, &menu->uiIcons.display.texture,
&menu->uiIcons.grass.texture, &menu->uiIcons.lighting.texture,
&menu->uiIcons.sky.texture, &menu->uiIcons.landscape.texture,
&menu->uiIcons.water.texture, &menu->uiIcons.debug.texture,
&menu->uiIcons.materials.texture, &menu->uiIcons.postProcessing.texture }) {
if (*texturePtr) {
(*texturePtr)->Release();
*texturePtr = nullptr;
// Release all existing textures using the same definitions list (avoids stale hardcoded list)
for (const auto& iconDef : iconDefs) {
if (*iconDef.texture) {
(*iconDef.texture)->Release();
*iconDef.texture = nullptr;
}
}
// Also release search icon (not in iconDefs)
if (menu->uiIcons.search.texture) {
menu->uiIcons.search.texture->Release();
menu->uiIcons.search.texture = nullptr;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

bool anyIconLoaded = false;
int iconsLoaded = 0;
Expand All @@ -219,24 +219,17 @@ namespace Util::IconLoader
anyIconLoaded = true;
} else {
// If monochrome icon failed to load, try fallback to colored version
if (basePath.find("Monochrome") != std::string::npos) {
std::string fallbackPath = basePath;
if (fullPath.find("Monochrome") != std::string::npos) {
std::string fallbackPath = fullPath;
size_t pos = fallbackPath.find("\\Monochrome");
if (pos != std::string::npos) {
fallbackPath.erase(pos, 11); // Remove "\Monochrome"
}
fallbackPath += iconDef.filename;
// Try to extract just the filename from iconDef.filename if it has path
size_t lastSlash = iconDef.filename.find_last_of("\\/");
if (lastSlash != std::string::npos) {
std::string justFilename = iconDef.filename.substr(lastSlash + 1);
fallbackPath = fallbackPath.substr(0, fallbackPath.find_last_of("\\/") + 1) + justFilename;
}
if (LoadTextureFromFile(device, fallbackPath.c_str(), iconDef.texture, *iconDef.size)) {
iconsLoaded++;
anyIconLoaded = true;
} else {
logger::warn("InitializeMenuIcons: Failed to load icon from: {} (and fallback)", fullPath);
logger::warn("InitializeMenuIcons: Failed to load icon from: {} (and fallback: {})", fullPath, fallbackPath);
}
} else {
logger::warn("InitializeMenuIcons: Failed to load icon from: {}", fullPath);
Expand Down
8 changes: 7 additions & 1 deletion src/Menu/OverlayRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,15 @@ void OverlayRenderer::RenderOverlay(
auto player = RE::PlayerCharacter::GetSingleton();
if (editorWindow->open && !(player && player->parentCell)) {
editorWindow->open = false;
if (editorWindow->IsInPreviewMode())
editorWindow->ExitPreviewMode();
}
if (editorWindow->open) {
ImGui::GetIO().MouseDrawCursor = true;
bool flying = editorWindow->IsPreviewFlying();
auto& io = ImGui::GetIO();
io.MouseDrawCursor = !flying;
if (flying)
io.MousePos = { -FLT_MAX, -FLT_MAX }; // prevent hover/tooltips during active flying
editorWindow->Draw();
} else if (menu.IsEnabled || HomePageRenderer::ShouldShowFirstTimeSetup()) {
ImGui::GetIO().MouseDrawCursor = true;
Expand Down
14 changes: 14 additions & 0 deletions src/Utils/UI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,20 @@ namespace Util
return StyledButtonWrapper(color, hover, active);
}

StyledButtonWrapper TransparentIconButtonStyle()
{
constexpr float kHoverAlpha = 0.25f;
auto hoverColor = Menu::GetSingleton()->GetTheme().Palette.Text;
hoverColor.w = kHoverAlpha;
return StyledButtonWrapper(ImVec4(0, 0, 0, 0), hoverColor, hoverColor);
}

ImVec4 GetIconTint()
{
const auto& theme = Menu::GetSingleton()->GetTheme();
return theme.UseMonochromeIcons ? theme.Palette.Text : ImVec4(1, 1, 1, 1);
}

// SectionWrapper implementation
SectionWrapper::SectionWrapper(const char* title, const char* description, const ImVec4& titleColor, bool isVisible) :
m_shouldDraw(isVisible),
Expand Down
8 changes: 8 additions & 0 deletions src/Utils/UI.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,14 @@ namespace Util
*/
StyledButtonWrapper ErrorButtonStyle();

/**
* Creates a transparent button with theme text color hover. Caller must push/pop FrameBorderSize=0 separately.
*/
StyledButtonWrapper TransparentIconButtonStyle();

/** Returns theme text color if monochrome icons enabled, otherwise white. */
ImVec4 GetIconTint();

/**
* Button with simple flash feedback (matches action icon hover effect style)
* @param label Button text
Expand Down
Loading
Loading