262 lines
9.1 KiB
C++

#include <vector>
#include <limits>
#include <libreborn/util.h>
#include "configuration.h"
#include <imgui_stdlib.h>
// Render Distances
static constexpr std::array render_distances = {
"Far",
"Normal",
"Short",
"Tiny"
};
// Tooltips/Text
static constexpr std::string revert_text = "Revert";
static constexpr std::string revert_tooltip_text = "Last Saved";
static constexpr std::string make_tooltip(const std::string &text, const std::string &type) {
return "Use " + text + ' ' + type;
}
// Construct
static constexpr int size = 400;
ConfigurationUI::ConfigurationUI(State &state_, bool &save_settings_):
Frame("Launcher", size, size),
original_state(state_),
state(state_),
save_settings(save_settings_) {}
// Render
int ConfigurationUI::render() {
bool on_servers_tab = false;
if (ImGui::BeginChild("Main", ImVec2(0, -ImGui::GetFrameHeightWithSpacing() /* Leave Room For Bottom Row */), ImGuiChildFlags_None, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
// Tabs
if (ImGui::BeginTabBar("TabBar")) {
// Main Tab
if (ImGui::BeginTabItem("General")) {
draw_main();
ImGui::EndTabItem();
}
// Advanced Tab
if (ImGui::BeginTabItem("Advanced")) {
draw_advanced();
ImGui::EndTabItem();
}
// Servers Tab
if (ImGui::BeginTabItem("Servers", nullptr, are_servers_unsaved() ? ImGuiTabItemFlags_UnsavedDocument : ImGuiTabItemFlags_None)) {
draw_servers();
ImGui::EndTabItem();
on_servers_tab = true;
}
ImGui::EndTabBar();
}
}
ImGui::EndChild();
// Bottom Row
return draw_bottom(on_servers_tab);
}
// Bottom Row
int ConfigurationUI::draw_bottom(const bool hide_reset_revert) const {
// Reset Settings
if (!hide_reset_revert) {
const State default_state;
constexpr const char *tooltip_type = "Settings";
std::vector<std::tuple<std::string, std::string, const State *>> reset_options = {
{revert_text, make_tooltip(revert_tooltip_text, tooltip_type), &original_state},
{"Reset", make_tooltip("Default", tooltip_type), &default_state},
};
for (const std::tuple<std::string, std::string, const State *> &option : reset_options) {
const State &new_state = *std::get<2>(option);
ImGui::BeginDisabled(state == new_state);
if (ImGui::Button(std::get<0>(option).c_str())) {
state = new_state;
}
ImGui::SetItemTooltip("%s", std::get<1>(option).c_str());
ImGui::EndDisabled();
ImGui::SameLine();
}
}
// Right-Align Buttons
int ret = 0;
bool unsaved_servers = are_servers_unsaved();
draw_right_aligned_buttons({quit_text, "Launch"}, [&ret, unsaved_servers](const int id, const bool was_clicked) {
if (id == 0) {
// Quit
if (was_clicked) {
ret = -1;
}
ImGui::SetItemTooltip("Changes Will Not Be Saved!");
// Disable Launch if Server List Is Unsaved
if (unsaved_servers) {
ImGui::BeginDisabled();
}
} else if (id == 1) {
// Launch
if (unsaved_servers) {
ImGui::SetItemTooltip("Server List Is Unsaved");
ImGui::EndDisabled();
}
if (was_clicked) {
ret = 1;
}
}
});
// Return
return ret;
}
// Main Tab
int ConfigurationUI::get_render_distance_index() const {
int render_distance_index = 0;
for (std::vector<std::string>::size_type i = 0; i < render_distances.size(); i++) {
if (std::string(render_distances[i]) == state.render_distance) {
render_distance_index = int(i);
break;
}
}
return render_distance_index;
}
void ConfigurationUI::draw_main() const {
const ImGuiStyle &style = ImGui::GetStyle();
const char *labels[] = {"Username", "Render Distance"};
// Calculate Label Size
float label_size = 0;
for (const char *label : labels) {
label_size = std::max(label_size, ImGui::CalcTextSize(label).x + style.ItemInnerSpacing.x);
}
ImGui::PushItemWidth(-label_size);
// Options
ImGui::InputText(labels[0], &state.username);
int render_distance_index = get_render_distance_index();
if (ImGui::Combo(labels[1], &render_distance_index, render_distances.data(), int(render_distances.size()))) {
state.render_distance = render_distances[render_distance_index];
}
ImGui::PopItemWidth();
ImGui::Checkbox("Save Settings On Launch", &save_settings);
}
// Advanced Tab
static std::string get_label_for_flag_node(const FlagNode &node) {
return node.name + "##FlagNode" + std::to_string(node.id);
}
void ConfigurationUI::draw_advanced() const {
if (ImGui::BeginChild("Features", ImVec2(0, 0), ImGuiChildFlags_Borders, ImGuiWindowFlags_HorizontalScrollbar)) {
// Categories
for (FlagNode &category : state.flags.root.children) {
const std::string label = get_label_for_flag_node(category);
if (ImGui::CollapsingHeader(label.c_str())) {
draw_category(category);
}
}
}
ImGui::EndChild();
}
void ConfigurationUI::draw_category(FlagNode &category) {
for (FlagNode &child : category.children) {
const std::string label = get_label_for_flag_node(child);
if (!child.children.empty()) {
// Sub-Category
if (ImGui::TreeNode(label.c_str())) {
draw_category(child);
ImGui::TreePop();
}
} else {
// Flag
ImGui::Checkbox(label.c_str(), &child.value);
}
}
}
// Servers
bool ConfigurationUI::are_servers_unsaved() const {
return servers.to_string() != last_saved_servers.to_string();
}
void ConfigurationUI::draw_servers() {
// Add/Clear
bool scroll_to_bottom = false;
if (ImGui::Button("Add")) {
servers.entries.emplace_back("", DEFAULT_MULTIPLAYER_PORT);
scroll_to_bottom = true;
}
ImGui::SameLine();
ImGui::BeginDisabled(servers.entries.empty());
if (ImGui::Button("Clear")) {
servers.entries.clear();
}
ImGui::EndDisabled();
ImGui::SameLine();
// Revert/Save
int clicked_button = -1;
ImGui::BeginDisabled(!are_servers_unsaved());
draw_right_aligned_buttons({revert_text.c_str(), "Save"}, [&clicked_button](const int id, const bool was_clicked) {
if (id == 0) {
ImGui::SetItemTooltip("%s", make_tooltip(revert_tooltip_text, "Server List").c_str());
}
if (was_clicked) {
clicked_button = id;
}
});
ImGui::EndDisabled();
if (clicked_button == 1) {
// Save
servers.save();
last_saved_servers = servers;
} else if (clicked_button == 0) {
// Revert To Last Saved Server List
servers = last_saved_servers;
}
// List
if (ImGui::BeginChild("ServerList", ImVec2(0, 0), ImGuiChildFlags_Borders)) {
draw_server_list();
if (scroll_to_bottom) {
ImGui::SetScrollHereY(1.0f);
}
}
ImGui::EndChild();
}
void ConfigurationUI::draw_server_list() {
for (std::vector<ServerList::Entry>::size_type i = 0; i < servers.entries.size(); ++i) {
ServerList::Entry &entry = servers.entries[i];
// Calculate Item Widths
const ImGuiStyle &style = ImGui::GetStyle();
const std::string port_width_text = std::to_string(int(std::numeric_limits<ServerList::port_t>::max()) * 2); // Should Comfortably Fit All Port Numbers
const std::string delete_text = "Delete";
const float port_width = get_frame_width(port_width_text.c_str());
const float width_needed = (style.ItemSpacing.x * 2.0f) + port_width + get_frame_width(delete_text.c_str());
// Labels
const std::string base_label = "##ServerEntry" + std::to_string(i);
// Hints
const char *address_hint = "Address";
const char *port_hint = "Port";
// Address
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x - width_needed);
ImGui::InputTextWithHint((base_label + address_hint).c_str(), address_hint, &entry.first, ImGuiInputTextFlags_CharsNoBlank);
ImGui::PopItemWidth();
// Port
ServerList::port_t &port = entry.second;
std::string port_str = port > 0 ? std::to_string(port) : "";
ImGui::SameLine();
ImGui::PushItemWidth(port_width);
if (ImGui::InputTextWithHint((base_label + port_hint).c_str(), port_hint, &port_str, ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_NoHorizontalScroll)) {
port = ServerList::parse_port(port_str);
}
ImGui::PopItemWidth();
// Delete
ImGui::SameLine();
if (ImGui::Button((delete_text + base_label).c_str())) {
servers.entries.erase(servers.entries.begin() + int(i));
i--;
}
}
}