LumixEngine/src/editor/settings.cpp

518 lines
13 KiB
C++
Raw Normal View History

#include "settings.h"
2019-06-13 17:26:52 +02:00
#include "engine/debug.h"
#include "engine/file_system.h"
#include "engine/engine.h"
2016-05-10 08:24:31 +02:00
#include "engine/log.h"
2018-11-11 13:46:42 +01:00
#include "engine/lua_wrapper.h"
2019-06-11 22:39:39 +02:00
#include "engine/os.h"
#include "editor/studio_app.h"
#include "editor/world_editor.h"
2015-12-23 19:52:09 +01:00
#include "imgui/imgui.h"
#include "utils.h"
#include <lua.hpp>
2017-05-23 19:57:11 +02:00
namespace Lumix
{
2016-03-05 22:58:55 +01:00
static const char DEFAULT_SETTINGS_PATH[] = "studio_default.ini";
static const char SETTINGS_PATH[] = "studio.ini";
2016-06-07 22:49:16 +02:00
static void loadStyle(lua_State* L)
{
2018-06-30 15:18:27 +02:00
lua_getglobal(L, "style");
if (lua_type(L, -1) == LUA_TTABLE)
2016-06-07 22:49:16 +02:00
{
auto& style = ImGui::GetStyle();
for (int i = 0; i < ImGuiCol_COUNT; ++i)
{
2017-10-19 00:20:37 +02:00
const char* name = ImGui::GetStyleColorName(i);
2018-06-30 15:18:27 +02:00
lua_getfield(L, -1, name);
if (lua_type(L, -1) == LUA_TTABLE)
{
2018-06-30 15:18:27 +02:00
lua_rawgeti(L, -1, 1);
if (lua_type(L, -1) == LUA_TNUMBER) style.Colors[i].x = (float)lua_tonumber(L, -1);
lua_rawgeti(L, -2, 2);
if (lua_type(L, -1) == LUA_TNUMBER) style.Colors[i].y = (float)lua_tonumber(L, -1);
lua_rawgeti(L, -3, 3);
if (lua_type(L, -1) == LUA_TNUMBER) style.Colors[i].z = (float)lua_tonumber(L, -1);
lua_rawgeti(L, -4, 4);
if (lua_type(L, -1) == LUA_TNUMBER) style.Colors[i].w = (float)lua_tonumber(L, -1);
lua_pop(L, 4);
}
2016-06-07 22:49:16 +02:00
lua_pop(L, 1);
}
2016-06-07 22:49:16 +02:00
}
lua_pop(L, 1);
}
2019-06-11 22:39:39 +02:00
static void saveStyle(OS::OutputFile& file)
2016-06-07 22:49:16 +02:00
{
auto& style = ImGui::GetStyle();
file << "style = {";
for (int i = 0; i < ImGuiCol_COUNT; ++i)
{
2017-10-19 00:20:37 +02:00
file << ImGui::GetStyleColorName(i) << " = {" << style.Colors[i].x
<< ", " << style.Colors[i].y
<< ", " << style.Colors[i].z
<< ", " << style.Colors[i].w << "},\n";
}
2016-06-07 22:49:16 +02:00
file << "}\n";
}
2018-12-09 11:36:31 +01:00
static void shortcutInput(OS::Keycode& shortcut)
{
2017-05-23 19:57:11 +02:00
StaticString<50> popup_name("");
popup_name << (i64)&shortcut;
2018-12-08 15:11:14 +01:00
char tmp[32];
2018-12-09 11:36:31 +01:00
OS::getKeyName(shortcut, tmp, sizeof(tmp));
2019-06-15 20:41:22 +02:00
StaticString<50> button_label(tmp[0] || shortcut == OS::Keycode::INVALID ? tmp : "Unknown");
2017-05-23 19:57:11 +02:00
button_label << "###" << (i64)&shortcut;
2018-12-09 11:36:31 +01:00
if (ImGui::Button(button_label, ImVec2(65, 0))) shortcut = OS::Keycode::INVALID;
2018-12-08 15:11:14 +01:00
if (ImGui::IsItemHovered()) {
2018-12-09 11:36:31 +01:00
for (int i = 0; i < (int)OS::Keycode::MAX; ++i) {
2019-06-15 20:41:22 +02:00
const auto kc= (OS::Keycode)i;
const bool is_mouse = kc == OS::Keycode::LBUTTON || kc == OS::Keycode::RBUTTON || kc == OS::Keycode::MBUTTON;
if (OS::isKeyDown(kc) && !is_mouse) {
2018-12-09 11:36:31 +01:00
shortcut = (OS::Keycode)i;
break;
}
}
}
}
static int getIntegerField(lua_State* L, const char* name, int default_value)
{
int value = default_value;
2018-06-30 15:18:27 +02:00
lua_getfield(L, -1, name);
if (lua_type(L, -1) == LUA_TNUMBER)
{
value = (int)lua_tointeger(L, -1);
}
lua_pop(L, 1);
return value;
}
static float getFloat(lua_State* L, const char* name, float default_value)
{
float value = default_value;
2018-06-30 15:18:27 +02:00
lua_getglobal(L, name);
if (lua_type(L, -1) == LUA_TNUMBER)
{
value = (float)lua_tonumber(L, -1);
}
lua_pop(L, 1);
return value;
}
static bool getBoolean(lua_State* L, const char* name, bool default_value)
{
bool value = default_value;
2018-06-30 15:18:27 +02:00
lua_getglobal(L, name);
if (lua_type(L, -1) == LUA_TBOOLEAN)
{
value = lua_toboolean(L, -1) != 0;
}
lua_pop(L, 1);
return value;
}
2015-09-29 20:48:26 +02:00
static int getInteger(lua_State* L, const char* name, int default_value)
{
int value = default_value;
2018-06-30 15:18:27 +02:00
lua_getglobal(L, name);
if (lua_type(L, -1) == LUA_TNUMBER)
2015-09-29 20:48:26 +02:00
{
value = (int)lua_tointeger(L, -1);
}
lua_pop(L, 1);
return value;
}
2016-06-12 23:24:33 +02:00
Settings::Settings(StudioApp& app)
: m_app(app)
2017-08-20 00:16:42 +02:00
, m_is_open(false)
2016-06-15 15:28:07 +02:00
, m_editor(nullptr)
, m_is_maximized(true)
2017-09-28 14:45:20 +02:00
, m_is_entity_list_open(false)
, m_is_entity_template_list_open(false)
, m_is_asset_browser_open(false)
, m_is_log_open(false)
, m_is_profiler_open(false)
, m_is_properties_open(false)
2016-06-15 15:28:07 +02:00
, m_is_crash_reporting_enabled(true)
, m_force_no_crash_report(false)
2017-09-23 22:15:11 +02:00
, m_mouse_sensitivity(80.0f, 80.0f)
, m_font_size(13)
{
m_filter[0] = 0;
m_window.x = m_window.y = 0;
m_window.w = m_window.h = -1;
2015-12-19 20:42:50 +01:00
m_state = luaL_newstate();
luaL_openlibs(m_state);
lua_newtable(m_state);
lua_setglobal(m_state, "custom");
}
2015-12-19 20:42:50 +01:00
Settings::~Settings()
{
2015-12-19 20:42:50 +01:00
lua_close(m_state);
}
2015-12-19 20:42:50 +01:00
2016-06-12 23:24:33 +02:00
bool Settings::load()
2015-12-19 20:42:50 +01:00
{
auto L = m_state;
2019-06-15 22:59:18 +02:00
OS::InputFile file;
FileSystem& fs = m_app.getWorldEditor().getEngine().getFileSystem();
2019-06-27 18:32:39 +02:00
const bool has_settings = fs.fileExists(SETTINGS_PATH);
2019-06-15 22:59:18 +02:00
const char* path = has_settings ? SETTINGS_PATH : DEFAULT_SETTINGS_PATH;
2019-07-08 22:57:24 +02:00
Array<u8> buf(m_app.getWorldEditor().getAllocator());
if (!fs.getContentSync(Path(path), &buf)) {
2019-06-21 17:14:06 +02:00
logError("Editor") << "Failed to open " << path;
2019-06-15 22:59:18 +02:00
return false;
}
2019-07-08 22:57:24 +02:00
StringView content((const char*)buf.begin(), buf.size());
2019-06-15 22:59:18 +02:00
if (!LuaWrapper::execute(L, content, "settings", 0)) {
2019-06-21 17:14:06 +02:00
logError("Editor") << path << ": " << lua_tostring(L, -1);
lua_pop(L, 1);
return false;
}
2018-06-30 15:18:27 +02:00
lua_getglobal(L, "window");
if (lua_type(L, -1) == LUA_TTABLE)
{
m_window.x = getIntegerField(L, "x", 0);
m_window.y = getIntegerField(L, "y", 0);
m_window.w = getIntegerField(L, "w", -1);
m_window.h = getIntegerField(L, "h", -1);
}
lua_pop(L, 1);
2016-06-07 22:49:16 +02:00
loadStyle(L);
m_is_maximized = getBoolean(L, "maximized", true);
2017-08-20 00:16:42 +02:00
m_is_open = getBoolean(L, "settings_opened", false);
2017-09-28 14:45:20 +02:00
m_is_asset_browser_open = getBoolean(L, "asset_browser_opened", false);
2018-12-24 16:15:22 +01:00
m_asset_browser_left_column_width = getFloat(L, "asset_browser_left_column_width", 100.f);
2017-09-28 14:45:20 +02:00
m_is_entity_list_open = getBoolean(L, "entity_list_opened", false);
m_is_entity_template_list_open = getBoolean(L, "entity_template_list_opened", false);
m_is_log_open = getBoolean(L, "log_opened", false);
m_is_profiler_open = getBoolean(L, "profiler_opened", false);
m_is_properties_open = getBoolean(L, "properties_opened", false);
m_is_crash_reporting_enabled = getBoolean(L, "error_reporting_enabled", true);
2017-05-23 19:57:11 +02:00
enableCrashReporting(m_is_crash_reporting_enabled && !m_force_no_crash_report);
2017-09-23 22:15:11 +02:00
m_mouse_sensitivity.x = getFloat(L, "mouse_sensitivity_x", 200.0f);
m_mouse_sensitivity.y = getFloat(L, "mouse_sensitivity_y", 200.0f);
m_font_size = getInteger(L, "font_size", 13);
2016-06-12 23:24:33 +02:00
auto& actions = m_app.getActions();
2018-06-30 15:18:27 +02:00
lua_getglobal(L, "actions");
if (lua_type(L, -1) == LUA_TTABLE)
{
2016-06-12 23:24:33 +02:00
for (int i = 0; i < actions.size(); ++i)
{
2018-06-30 15:18:27 +02:00
lua_getfield(L, -1, actions[i]->name);
if (lua_type(L, -1) == LUA_TTABLE)
{
2017-05-23 19:57:11 +02:00
for (int j = 0; j < lengthOf(actions[i]->shortcut); ++j)
{
2018-06-30 15:18:27 +02:00
lua_rawgeti(L, -1, 1 + j);
if (lua_type(L, -1) == LUA_TNUMBER)
{
2018-12-09 11:36:31 +01:00
actions[i]->shortcut[j] = (OS::Keycode)lua_tointeger(L, -1);
}
lua_pop(L, 1);
}
2016-06-12 23:24:33 +02:00
}
lua_pop(L, 1);
}
}
lua_pop(L, 1);
m_app.getToolbarActions().clear();
2018-06-30 15:18:27 +02:00
lua_getglobal(L, "toolbar");
if (lua_type(L, -1) == LUA_TTABLE)
2016-06-12 23:24:33 +02:00
{
2018-11-11 13:46:42 +01:00
LuaWrapper::forEachArrayItem<const char*>(L, -1, nullptr, [this](const char* action_name){
Action* action = m_app.getAction(action_name);
if(action) m_app.getToolbarActions().push(action);
});
}
lua_pop(L, 1);
2016-01-01 20:31:34 +01:00
return true;
}
2017-11-28 19:37:15 +01:00
void Settings::setValue(const char* name, bool value) const
2015-12-11 20:48:47 +01:00
{
2015-12-19 20:42:50 +01:00
lua_getglobal(m_state, "custom");
lua_pushboolean(m_state, value);
lua_setfield(m_state, -2, name);
lua_pop(m_state, 1);
2015-12-11 20:48:47 +01:00
}
2017-11-28 19:37:15 +01:00
void Settings::setValue(const char* name, int value) const
2015-12-19 20:42:50 +01:00
{
lua_getglobal(m_state, "custom");
lua_pushinteger(m_state, value);
lua_setfield(m_state, -2, name);
lua_pop(m_state, 1);
2015-12-19 20:42:50 +01:00
}
int Settings::getValue(const char* name, int default_value) const
2015-12-19 20:42:50 +01:00
{
lua_getglobal(m_state, "custom");
int v = getIntegerField(m_state, name, default_value);
2015-12-19 20:42:50 +01:00
lua_pop(m_state, 1);
return v;
}
bool Settings::getValue(const char* name, bool default_value) const
2015-12-19 20:42:50 +01:00
{
bool v = default_value;
2015-12-19 20:42:50 +01:00
lua_getglobal(m_state, "custom");
2018-06-30 15:18:27 +02:00
lua_getfield(m_state, -1, name);
if (lua_type(m_state, -1) == LUA_TBOOLEAN)
2015-12-19 20:42:50 +01:00
{
v = lua_toboolean(m_state, -1) != 0;
}
2016-01-24 18:14:37 +01:00
lua_pop(m_state, 2);
2015-12-19 20:42:50 +01:00
return v;
}
2016-06-12 23:24:33 +02:00
bool Settings::save()
2015-12-19 20:42:50 +01:00
{
2016-06-12 23:24:33 +02:00
auto& actions = m_app.getActions();
2019-06-11 22:39:39 +02:00
OS::OutputFile file;
FileSystem& fs = m_app.getWorldEditor().getEngine().getFileSystem();
if (!fs.open(SETTINGS_PATH, &file)) return false;
2015-12-11 20:48:47 +01:00
file << "window = { x = " << m_window.x
<< ", y = " << m_window.y
<< ", w = " << m_window.w
<< ", h = " << m_window.h << " }\n";
file << "maximized = " << (m_is_maximized ? "true" : "false") << "\n";
auto writeBool = [&file](const char* name, bool value) {
file << name << " = " << (value ? "true\n" : "false\n");
};
2017-08-20 00:16:42 +02:00
writeBool("settings_opened", m_is_open);
2017-09-28 14:45:20 +02:00
writeBool("asset_browser_opened", m_is_asset_browser_open);
writeBool("entity_list_opened", m_is_entity_list_open);
writeBool("entity_template_list_opened", m_is_entity_template_list_open);
writeBool("log_opened", m_is_log_open);
writeBool("profiler_opened", m_is_profiler_open);
writeBool("properties_opened", m_is_properties_open);
writeBool("error_reporting_enabled", m_is_crash_reporting_enabled);
2017-09-23 22:15:11 +02:00
file << "mouse_sensitivity_x = " << m_mouse_sensitivity.x << "\n";
file << "mouse_sensitivity_y = " << m_mouse_sensitivity.y << "\n";
file << "font_size = " << m_font_size << "\n";
2017-08-20 00:16:42 +02:00
file << "asset_browser_left_column_width = " << m_asset_browser_left_column_width << "\n";
2016-06-07 22:49:16 +02:00
saveStyle(file);
file << "custom = {\n";
2015-12-19 20:42:50 +01:00
lua_getglobal(m_state, "custom");
lua_pushnil(m_state);
bool first = true;
2015-12-19 20:42:50 +01:00
while (lua_next(m_state, -2))
{
if (!first) file << ",\n";
2015-12-19 20:42:50 +01:00
const char* name = lua_tostring(m_state, -2);
switch (lua_type(m_state, -1))
{
case LUA_TBOOLEAN:
file << name << " = " << (lua_toboolean(m_state, -1) != 0 ? "true" : "false");
break;
case LUA_TNUMBER:
file << name << " = " << (int)lua_tonumber(m_state, -1);
break;
default:
ASSERT(false);
break;
2015-12-19 20:42:50 +01:00
}
lua_pop(m_state, 1);
first = false;
2015-12-19 20:42:50 +01:00
}
lua_pop(m_state, 1);
file << "}\n";
2015-12-11 20:48:47 +01:00
file << "actions = {\n";
2016-06-12 23:24:33 +02:00
for (int i = 0; i < actions.size(); ++i)
{
file << "\t" << actions[i]->name << " = {"
2018-12-08 15:11:14 +01:00
<< (int)actions[i]->shortcut[0] << ", "
<< (int)actions[i]->shortcut[1] << ", "
<< (int)actions[i]->shortcut[2] << "},\n";
2016-06-12 23:24:33 +02:00
}
file << "}\n";
file << "toolbar = {\n";
for (auto* action : m_app.getToolbarActions())
{
file << "\t\"" << action->name << "\",\n";
}
2015-12-11 20:48:47 +01:00
file << "}\n";
2016-01-01 20:31:34 +01:00
2015-12-11 20:48:47 +01:00
file.close();
return true;
}
2017-11-28 19:37:15 +01:00
void Settings::showToolbarSettings() const
2016-06-12 23:24:33 +02:00
{
auto& actions = m_app.getToolbarActions();
2016-06-13 01:51:24 +02:00
static Action* dragged = nullptr;
ImVec4 tint_color = ImGui::GetStyle().Colors[ImGuiCol_Text];
2016-06-13 01:51:24 +02:00
for (auto* action : actions)
2016-06-12 23:24:33 +02:00
{
2018-08-19 22:01:20 +02:00
int* t = (int*)action->icon;
ImGui::ImageButton((void*)(uintptr_t)*t, ImVec2(24, 24), ImVec2(0, 0), ImVec2(1, 1), -1, ImVec4(0, 0, 0, 0), tint_color);
2016-06-14 10:24:32 +02:00
if (dragged && ImGui::IsItemHovered() && ImGui::IsMouseReleased(0))
2016-06-12 23:24:33 +02:00
{
2016-06-13 01:51:24 +02:00
actions.insert(actions.indexOf(action), dragged);
dragged = nullptr;
break;
2016-06-12 23:24:33 +02:00
}
2016-06-13 01:51:24 +02:00
if (ImGui::IsItemActive() && ImGui::IsMouseDragging())
2016-06-12 23:24:33 +02:00
{
2016-06-13 01:51:24 +02:00
dragged = action;
actions.eraseItem(action);
2016-06-12 23:24:33 +02:00
break;
}
2016-06-13 01:51:24 +02:00
ImGui::SameLine();
2016-06-12 23:24:33 +02:00
}
2016-06-13 01:51:24 +02:00
ImGui::NewLine();
if (dragged) ImGui::SetTooltip("%s", dragged->label_long);
2016-06-13 01:51:24 +02:00
if (ImGui::IsMouseReleased(0)) dragged = nullptr;
2016-06-12 23:24:33 +02:00
static int tmp = 0;
auto getter = [](void* data, int idx, const char** out) -> bool {
Action** tools = (Action**)data;
*out = tools[idx]->label_long;
2016-06-12 23:24:33 +02:00
return true;
};
Action* tools[1024];
int count = 0;
for (auto* action : m_app.getActions())
{
2016-06-13 01:51:24 +02:00
if (action->icon && action->is_global)
2016-06-12 23:24:33 +02:00
{
tools[count] = action;
++count;
}
}
ImGui::Combo("", &tmp, getter, tools, count);
ImGui::SameLine();
if (ImGui::Button("Add"))
{
actions.push(tools[tmp]);
}
}
void Settings::showShortcutSettings()
{
2016-06-12 23:24:33 +02:00
auto& actions = m_app.getActions();
2017-10-19 17:48:53 +02:00
ImGui::LabellessInputText("Filter", m_filter, lengthOf(m_filter));
2016-06-12 23:24:33 +02:00
ImGui::Columns(4);
2016-06-09 00:09:13 +02:00
ImGui::Text("Label");
ImGui::NextColumn();
ImGui::Text("Shortcut key 1");
ImGui::NextColumn();
ImGui::Text("Shortcut key 2");
ImGui::NextColumn();
ImGui::Text("Shortcut key 3");
ImGui::NextColumn();
ImGui::Separator();
2016-06-12 23:24:33 +02:00
for (int i = 0; i < actions.size(); ++i)
{
Action& a = *actions[i];
if (m_filter[0] == 0 || stristr(a.label_long, m_filter) != 0)
{
2017-10-19 00:20:37 +02:00
ImGui::AlignTextToFramePadding();
ImGui::Text("%s", a.label_long);
ImGui::NextColumn();
shortcutInput(a.shortcut[0]);
ImGui::NextColumn();
shortcutInput(a.shortcut[1]);
ImGui::NextColumn();
shortcutInput(a.shortcut[2]);
ImGui::NextColumn();
}
}
ImGui::Columns(1);
2016-06-12 23:24:33 +02:00
}
2016-06-12 23:24:33 +02:00
void Settings::onGUI()
{
2018-10-06 12:44:38 +02:00
if (!m_is_open) return;
if (ImGui::Begin("Settings", &m_is_open))
{
2016-06-12 23:24:33 +02:00
if (ImGui::Button("Save")) save();
ImGui::SameLine();
2016-06-12 23:24:33 +02:00
if (ImGui::Button("Reload")) load();
ImGui::SameLine();
ImGui::Text("Settings are saved when the application closes");
if (ImGui::CollapsingHeader("General"))
{
if (m_force_no_crash_report)
{
ImGui::Text("Crash reporting disabled from command line");
}
else
{
if (ImGui::Checkbox("Crash reporting", &m_is_crash_reporting_enabled))
{
2017-05-23 19:57:11 +02:00
enableCrashReporting(m_is_crash_reporting_enabled);
}
}
2017-09-23 22:15:11 +02:00
ImGui::DragFloat2("Mouse sensitivity", &m_mouse_sensitivity.x, 0.1f, 500.0f);
}
2015-09-29 20:48:26 +02:00
2016-06-12 23:24:33 +02:00
if (ImGui::CollapsingHeader("Shortcuts")) showShortcutSettings();
if (ImGui::CollapsingHeader("Toolbar")) showToolbarSettings();
2016-06-07 18:51:49 +02:00
if (ImGui::CollapsingHeader("Style"))
{
2017-10-30 23:39:34 +01:00
ImGui::InputInt("Font size (needs restart)", &m_font_size);
2016-06-07 18:51:49 +02:00
ImGui::ShowStyleEditor();
}
}
2018-10-06 12:44:38 +02:00
ImGui::End();
}
2017-05-23 19:57:11 +02:00
} // namespace Lumix