mirror of https://github.com/xemu-project/xemu.git
1147 lines
38 KiB
C++
1147 lines
38 KiB
C++
//
|
|
// xemu User Interface
|
|
//
|
|
// Copyright (C) 2020-2022 Matt Borgerson
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
#include "common.hh"
|
|
#include "scene-manager.hh"
|
|
#include "widgets.hh"
|
|
#include "main-menu.hh"
|
|
#include "font-manager.hh"
|
|
#include "input-manager.hh"
|
|
#include "viewport-manager.hh"
|
|
#include "xemu-hud.h"
|
|
#include "misc.hh"
|
|
#include "gl-helpers.hh"
|
|
#include "reporting.hh"
|
|
|
|
#include "../xemu-input.h"
|
|
#include "../xemu-notifications.h"
|
|
#include "../xemu-settings.h"
|
|
#include "../xemu-monitor.h"
|
|
#include "../xemu-version.h"
|
|
#include "../xemu-net.h"
|
|
#include "../xemu-os-utils.h"
|
|
#include "../xemu-xbe.h"
|
|
|
|
MainMenuScene g_main_menu;
|
|
|
|
MainMenuTabView::~MainMenuTabView() {}
|
|
void MainMenuTabView::Draw() {}
|
|
|
|
void MainMenuGeneralView::Draw()
|
|
{
|
|
SectionTitle("Updates");
|
|
Toggle("Check for updates", &g_config.general.updates.check,
|
|
"Check for updates whenever xemu is opened");
|
|
|
|
SectionTitle("Performance");
|
|
Toggle("Hard FPU emulation", &g_config.perf.hard_fpu,
|
|
"Use hardware-accelerated floating point emulation (requires restart)");
|
|
// toggle("Cache shaders to disk", &g_config.perf.cache_shaders,
|
|
// "Reduce stutter in games by caching previously generated shaders");
|
|
|
|
SectionTitle("Miscellaneous");
|
|
Toggle("Skip startup animation", &g_config.general.skip_boot_anim,
|
|
"Skip the full Xbox boot animation sequence");
|
|
FilePicker("Screenshot output directory", &g_config.general.screenshot_dir,
|
|
NULL, true);
|
|
// toggle("Throttle DVD/HDD speeds", &g_config.general.throttle_io,
|
|
// "Limit DVD/HDD throughput to approximate Xbox load times");
|
|
}
|
|
|
|
void MainMenuInputView::Draw()
|
|
{
|
|
SectionTitle("Controllers");
|
|
ImGui::PushFont(g_font_mgr.m_menu_font_small);
|
|
|
|
static int active = 0;
|
|
|
|
// Output dimensions of texture
|
|
float t_w = 512, t_h = 512;
|
|
// Dimensions of (port+label)s
|
|
float b_x = 0, b_x_stride = 100, b_y = 400;
|
|
float b_w = 68, b_h = 81;
|
|
// Dimensions of controller (rendered at origin)
|
|
float controller_width = 477.0f;
|
|
float controller_height = 395.0f;
|
|
|
|
// Setup rendering to fbo for controller and port images
|
|
controller_fbo->Target();
|
|
ImTextureID id = (ImTextureID)(intptr_t)controller_fbo->Texture();
|
|
|
|
//
|
|
// Render buttons with icons of the Xbox style port sockets with
|
|
// circular numbers above them. These buttons can be activated to
|
|
// configure the associated port, like a tabbed interface.
|
|
//
|
|
ImVec4 color_active(0.50, 0.86, 0.54, 0.12);
|
|
ImVec4 color_inactive(0, 0, 0, 0);
|
|
|
|
// Begin a 4-column layout to render the ports
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,
|
|
g_viewport_mgr.Scale(ImVec2(0, 12)));
|
|
ImGui::Columns(4, "mixed", false);
|
|
|
|
const int port_padding = 8;
|
|
for (int i = 0; i < 4; i++) {
|
|
bool is_selected = (i == active);
|
|
bool port_is_bound = (xemu_input_get_bound(i) != NULL);
|
|
|
|
// Set an X offset to center the image button within the column
|
|
ImGui::SetCursorPosX(
|
|
ImGui::GetCursorPosX() +
|
|
(int)((ImGui::GetColumnWidth() - b_w * g_viewport_mgr.m_scale -
|
|
2 * port_padding * g_viewport_mgr.m_scale) /
|
|
2));
|
|
|
|
// We are using the same texture for all buttons, but ImageButton
|
|
// uses the texture as a unique ID. Push a new ID now to resolve
|
|
// the conflict.
|
|
ImGui::PushID(i);
|
|
float x = b_x+i*b_x_stride;
|
|
ImGui::PushStyleColor(ImGuiCol_Button, is_selected ?
|
|
color_active :
|
|
color_inactive);
|
|
bool activated = ImGui::ImageButton(id,
|
|
ImVec2(b_w*g_viewport_mgr.m_scale,b_h*g_viewport_mgr.m_scale),
|
|
ImVec2(x/t_w, (b_y+b_h)/t_h),
|
|
ImVec2((x+b_w)/t_w, b_y/t_h),
|
|
port_padding * g_viewport_mgr.m_scale);
|
|
ImGui::PopStyleColor();
|
|
|
|
if (activated) {
|
|
active = i;
|
|
}
|
|
|
|
uint32_t port_color = 0xafafafff;
|
|
bool is_hovered = ImGui::IsItemHovered();
|
|
if (is_hovered) {
|
|
port_color = 0xffffffff;
|
|
} else if (is_selected || port_is_bound) {
|
|
port_color = 0x81dc8a00;
|
|
}
|
|
|
|
RenderControllerPort(x, b_y, i, port_color);
|
|
|
|
ImGui::PopID();
|
|
ImGui::NextColumn();
|
|
}
|
|
ImGui::PopStyleVar(); // ItemSpacing
|
|
ImGui::Columns(1);
|
|
|
|
//
|
|
// Render input device combo
|
|
//
|
|
|
|
// List available input devices
|
|
const char *not_connected = "Not Connected";
|
|
ControllerState *bound_state = xemu_input_get_bound(active);
|
|
|
|
// Get current controller name
|
|
const char *name;
|
|
if (bound_state == NULL) {
|
|
name = not_connected;
|
|
} else {
|
|
name = bound_state->name;
|
|
}
|
|
|
|
ImGui::SetNextItemWidth(-FLT_MIN);
|
|
if (ImGui::BeginCombo("###InputDevices", name, ImGuiComboFlags_NoArrowButton))
|
|
{
|
|
// Handle "Not connected"
|
|
bool is_selected = bound_state == NULL;
|
|
if (ImGui::Selectable(not_connected, is_selected)) {
|
|
xemu_input_bind(active, NULL, 1);
|
|
bound_state = NULL;
|
|
}
|
|
if (is_selected) {
|
|
ImGui::SetItemDefaultFocus();
|
|
}
|
|
|
|
// Handle all available input devices
|
|
ControllerState *iter;
|
|
QTAILQ_FOREACH(iter, &available_controllers, entry) {
|
|
is_selected = bound_state == iter;
|
|
ImGui::PushID(iter);
|
|
const char *selectable_label = iter->name;
|
|
char buf[128];
|
|
if (iter->bound >= 0) {
|
|
snprintf(buf, sizeof(buf), "%s (Port %d)", iter->name, iter->bound+1);
|
|
selectable_label = buf;
|
|
}
|
|
if (ImGui::Selectable(selectable_label, is_selected)) {
|
|
xemu_input_bind(active, iter, 1);
|
|
bound_state = iter;
|
|
}
|
|
if (is_selected) {
|
|
ImGui::SetItemDefaultFocus();
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::EndCombo();
|
|
}
|
|
DrawComboChevron();
|
|
|
|
ImGui::Columns(1);
|
|
|
|
//
|
|
// Add a separator between input selection and controller graphic
|
|
//
|
|
ImGui::Dummy(ImVec2(0.0f, ImGui::GetStyle().WindowPadding.y / 2));
|
|
|
|
//
|
|
// Render controller image
|
|
//
|
|
bool device_selected = false;
|
|
|
|
if (bound_state) {
|
|
device_selected = true;
|
|
RenderController(0, 0, 0x81dc8a00, 0x0f0f0f00, bound_state);
|
|
} else {
|
|
static ControllerState state = { 0 };
|
|
RenderController(0, 0, 0x1f1f1f00, 0x0f0f0f00, &state);
|
|
}
|
|
|
|
ImVec2 cur = ImGui::GetCursorPos();
|
|
|
|
ImVec2 controller_display_size;
|
|
if (ImGui::GetContentRegionMax().x < controller_width*g_viewport_mgr.m_scale) {
|
|
controller_display_size.x = ImGui::GetContentRegionMax().x;
|
|
controller_display_size.y =
|
|
controller_display_size.x * controller_height / controller_width;
|
|
} else {
|
|
controller_display_size =
|
|
ImVec2(controller_width * g_viewport_mgr.m_scale,
|
|
controller_height * g_viewport_mgr.m_scale);
|
|
}
|
|
|
|
ImGui::SetCursorPosX(
|
|
ImGui::GetCursorPosX() +
|
|
(int)((ImGui::GetColumnWidth() - controller_display_size.x) / 2.0));
|
|
|
|
ImGui::Image(id,
|
|
controller_display_size,
|
|
ImVec2(0, controller_height/t_h),
|
|
ImVec2(controller_width/t_w, 0));
|
|
ImVec2 pos = ImGui::GetCursorPos();
|
|
if (!device_selected) {
|
|
const char *msg = "Please select an available input device";
|
|
ImVec2 dim = ImGui::CalcTextSize(msg);
|
|
ImGui::SetCursorPosX(cur.x + (controller_display_size.x-dim.x)/2);
|
|
ImGui::SetCursorPosY(cur.y + (controller_display_size.y-dim.y)/2);
|
|
ImGui::Text("%s", msg);
|
|
}
|
|
|
|
controller_fbo->Restore();
|
|
|
|
ImGui::PopFont();
|
|
ImGui::SetCursorPos(pos);
|
|
|
|
SectionTitle("Options");
|
|
Toggle("Auto-bind controllers", &g_config.input.auto_bind,
|
|
"Bind newly connected controllers to any open port");
|
|
Toggle("Background controller input capture",
|
|
&g_config.input.background_input_capture,
|
|
"Capture even if window is unfocused (requires restart)");
|
|
}
|
|
|
|
void MainMenuDisplayView::Draw()
|
|
{
|
|
SectionTitle("Quality");
|
|
int rendering_scale = nv2a_get_surface_scale_factor() - 1;
|
|
if (ChevronCombo("Internal resolution scale", &rendering_scale,
|
|
"1x\0"
|
|
"2x\0"
|
|
"3x\0"
|
|
"4x\0"
|
|
"5x\0"
|
|
"6x\0"
|
|
"7x\0"
|
|
"8x\0"
|
|
"9x\0"
|
|
"10x\0",
|
|
"Increase surface scaling factor for higher quality")) {
|
|
nv2a_set_surface_scale_factor(rendering_scale+1);
|
|
}
|
|
|
|
SectionTitle("Window");
|
|
bool fs = xemu_is_fullscreen();
|
|
if (Toggle("Fullscreen", &fs, "Enable fullscreen now")) {
|
|
xemu_toggle_fullscreen();
|
|
}
|
|
Toggle("Fullscreen on startup",
|
|
&g_config.display.window.fullscreen_on_startup,
|
|
"Start xemu in fullscreen when opened");
|
|
if (ChevronCombo("Window size", &g_config.display.window.startup_size,
|
|
"Last Used\0"
|
|
"640x480\0"
|
|
"1280x720\0"
|
|
"1280x800\0"
|
|
"1280x960\0"
|
|
"1920x1080\0"
|
|
"2560x1440\0"
|
|
"2560x1600\0"
|
|
"2560x1920\0"
|
|
"3840x2160\0",
|
|
"Select preferred startup window size")) {
|
|
}
|
|
Toggle("Vertical refresh sync", &g_config.display.window.vsync,
|
|
"Sync to screen vertical refresh to reduce tearing artifacts");
|
|
|
|
SectionTitle("Interface");
|
|
Toggle("Show main menu bar", &g_config.display.ui.show_menubar,
|
|
"Show main menu bar when mouse is activated");
|
|
|
|
int ui_scale_idx;
|
|
if (g_config.display.ui.auto_scale) {
|
|
ui_scale_idx = 0;
|
|
} else {
|
|
ui_scale_idx = g_config.display.ui.scale;
|
|
if (ui_scale_idx < 0) ui_scale_idx = 0;
|
|
else if (ui_scale_idx > 2) ui_scale_idx = 2;
|
|
}
|
|
if (ChevronCombo("UI scale", &ui_scale_idx,
|
|
"Auto\0"
|
|
"1x\0"
|
|
"2x\0",
|
|
"Interface element scale")) {
|
|
if (ui_scale_idx == 0) {
|
|
g_config.display.ui.auto_scale = true;
|
|
} else {
|
|
g_config.display.ui.auto_scale = false;
|
|
g_config.display.ui.scale = ui_scale_idx;
|
|
}
|
|
}
|
|
Toggle("Animations", &g_config.display.ui.use_animations,
|
|
"Enable xemu user interface animations");
|
|
ChevronCombo("Display mode", &g_config.display.ui.fit,
|
|
"Center\0"
|
|
"Scale\0"
|
|
"Scale (Widescreen 16:9)\0"
|
|
"Scale (4:3)\0"
|
|
"Stretch\0",
|
|
"Select how the framebuffer should fit or scale into the window");
|
|
}
|
|
|
|
void MainMenuAudioView::Draw()
|
|
{
|
|
SectionTitle("Volume");
|
|
char buf[32];
|
|
snprintf(buf, sizeof(buf), "Limit output volume (%d%%)",
|
|
(int)(g_config.audio.volume_limit * 100));
|
|
Slider("Output volume limit", &g_config.audio.volume_limit, buf);
|
|
|
|
SectionTitle("Quality");
|
|
Toggle("Real-time DSP processing", &g_config.audio.use_dsp,
|
|
"Enable improved audio accuracy (experimental)");
|
|
|
|
}
|
|
|
|
NetworkInterface::NetworkInterface(pcap_if_t *pcap_desc, char *_friendlyname)
|
|
{
|
|
m_pcap_name = pcap_desc->name;
|
|
m_description = pcap_desc->description ?: pcap_desc->name;
|
|
if (_friendlyname) {
|
|
char *tmp =
|
|
g_strdup_printf("%s (%s)", _friendlyname, m_description.c_str());
|
|
m_friendly_name = tmp;
|
|
g_free((gpointer)tmp);
|
|
} else {
|
|
m_friendly_name = m_description;
|
|
}
|
|
}
|
|
|
|
NetworkInterfaceManager::NetworkInterfaceManager()
|
|
{
|
|
m_current_iface = NULL;
|
|
m_failed_to_load_lib = false;
|
|
}
|
|
|
|
void NetworkInterfaceManager::Refresh(void)
|
|
{
|
|
pcap_if_t *alldevs, *iter;
|
|
char err[PCAP_ERRBUF_SIZE];
|
|
|
|
if (xemu_net_is_enabled()) {
|
|
return;
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
if (pcap_load_library()) {
|
|
m_failed_to_load_lib = true;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
m_ifaces.clear();
|
|
m_current_iface = NULL;
|
|
|
|
if (pcap_findalldevs(&alldevs, err)) {
|
|
return;
|
|
}
|
|
|
|
for (iter=alldevs; iter != NULL; iter=iter->next) {
|
|
#if defined(_WIN32)
|
|
char *friendly_name = get_windows_interface_friendly_name(iter->name);
|
|
m_ifaces.emplace_back(new NetworkInterface(iter, friendly_name));
|
|
if (friendly_name) {
|
|
g_free((gpointer)friendly_name);
|
|
}
|
|
#else
|
|
m_ifaces.emplace_back(new NetworkInterface(iter));
|
|
#endif
|
|
if (!strcmp(g_config.net.pcap.netif, iter->name)) {
|
|
m_current_iface = m_ifaces.back().get();
|
|
}
|
|
}
|
|
|
|
pcap_freealldevs(alldevs);
|
|
}
|
|
|
|
void NetworkInterfaceManager::Select(NetworkInterface &iface)
|
|
{
|
|
m_current_iface = &iface;
|
|
xemu_settings_set_string(&g_config.net.pcap.netif,
|
|
iface.m_pcap_name.c_str());
|
|
}
|
|
|
|
bool NetworkInterfaceManager::IsCurrent(NetworkInterface &iface)
|
|
{
|
|
return &iface == m_current_iface;
|
|
}
|
|
|
|
MainMenuNetworkView::MainMenuNetworkView()
|
|
{
|
|
should_refresh = true;
|
|
}
|
|
|
|
void MainMenuNetworkView::Draw()
|
|
{
|
|
SectionTitle("Adapter");
|
|
bool enabled = xemu_net_is_enabled();
|
|
g_config.net.enable = enabled;
|
|
if (Toggle("Enable", &g_config.net.enable,
|
|
enabled ? "Virtual network connected (disable to change network "
|
|
"settings)" :
|
|
"Connect virtual network cable to machine")) {
|
|
if (enabled) {
|
|
xemu_net_disable();
|
|
} else {
|
|
xemu_net_enable();
|
|
}
|
|
}
|
|
|
|
bool appearing = ImGui::IsWindowAppearing();
|
|
if (enabled) ImGui::BeginDisabled();
|
|
if (ChevronCombo(
|
|
"Attached to", &g_config.net.backend,
|
|
"NAT\0"
|
|
"UDP Tunnel\0"
|
|
"Bridged Adapter\0",
|
|
"Controls what the virtual network controller interfaces with")) {
|
|
appearing = true;
|
|
}
|
|
SectionTitle("Options");
|
|
switch (g_config.net.backend) {
|
|
case CONFIG_NET_BACKEND_PCAP:
|
|
DrawPcapOptions(appearing);
|
|
break;
|
|
case CONFIG_NET_BACKEND_NAT:
|
|
DrawNatOptions(appearing);
|
|
break;
|
|
case CONFIG_NET_BACKEND_UDP:
|
|
DrawUdpOptions(appearing);
|
|
break;
|
|
default: break;
|
|
}
|
|
if (enabled) ImGui::EndDisabled();
|
|
}
|
|
|
|
void MainMenuNetworkView::DrawPcapOptions(bool appearing)
|
|
{
|
|
if (iface_mgr.get() == nullptr) {
|
|
iface_mgr.reset(new NetworkInterfaceManager());
|
|
iface_mgr->Refresh();
|
|
}
|
|
|
|
if (iface_mgr->m_failed_to_load_lib) {
|
|
#if defined(_WIN32)
|
|
const char *msg = "npcap library could not be loaded.\n"
|
|
"To use this backend, please install npcap.";
|
|
ImGui::Text("%s", msg);
|
|
ImGui::Dummy(ImVec2(0,10*g_viewport_mgr.m_scale));
|
|
ImGui::SetCursorPosX((ImGui::GetWindowWidth()-120*g_viewport_mgr.m_scale)/2);
|
|
if (ImGui::Button("Install npcap", ImVec2(120*g_viewport_mgr.m_scale, 0))) {
|
|
xemu_open_web_browser("https://nmap.org/npcap/");
|
|
}
|
|
#endif
|
|
} else {
|
|
const char *selected_display_name =
|
|
(iface_mgr->m_current_iface ?
|
|
iface_mgr->m_current_iface->m_friendly_name.c_str() :
|
|
g_config.net.pcap.netif);
|
|
float combo_width = ImGui::GetColumnWidth();
|
|
float combo_size_ratio = 0.5;
|
|
combo_width *= combo_size_ratio;
|
|
PrepareComboTitleDescription("Network interface",
|
|
"Host network interface to bridge with",
|
|
combo_size_ratio);
|
|
ImGui::SetNextItemWidth(combo_width);
|
|
ImGui::PushFont(g_font_mgr.m_menu_font_small);
|
|
if (ImGui::BeginCombo("###network_iface", selected_display_name,
|
|
ImGuiComboFlags_NoArrowButton)) {
|
|
if (should_refresh) {
|
|
iface_mgr->Refresh();
|
|
should_refresh = false;
|
|
}
|
|
|
|
int i = 0;
|
|
for (auto &iface : iface_mgr->m_ifaces) {
|
|
bool is_selected = iface_mgr->IsCurrent((*iface));
|
|
ImGui::PushID(i++);
|
|
if (ImGui::Selectable(iface->m_friendly_name.c_str(),
|
|
is_selected)) {
|
|
iface_mgr->Select((*iface));
|
|
}
|
|
if (is_selected) ImGui::SetItemDefaultFocus();
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::EndCombo();
|
|
} else {
|
|
should_refresh = true;
|
|
}
|
|
ImGui::PopFont();
|
|
DrawComboChevron();
|
|
}
|
|
}
|
|
|
|
void MainMenuNetworkView::DrawNatOptions(bool appearing)
|
|
{
|
|
static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;
|
|
WidgetTitleDescriptionItem(
|
|
"Port Forwarding",
|
|
"Configure xemu to forward connections to guest on these ports");
|
|
float p = ImGui::GetFrameHeight() * 0.3;
|
|
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(p, p));
|
|
if (ImGui::BeginTable("port_forward_tbl", 4, flags))
|
|
{
|
|
ImGui::TableSetupColumn("Host Port");
|
|
ImGui::TableSetupColumn("Guest Port");
|
|
ImGui::TableSetupColumn("Protocol");
|
|
ImGui::TableSetupColumn("Action");
|
|
ImGui::TableHeadersRow();
|
|
|
|
for (unsigned int row = 0; row < g_config.net.nat.forward_ports_count; row++)
|
|
{
|
|
ImGui::TableNextRow();
|
|
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGui::Text("%d", g_config.net.nat.forward_ports[row].host);
|
|
|
|
ImGui::TableSetColumnIndex(1);
|
|
ImGui::Text("%d", g_config.net.nat.forward_ports[row].guest);
|
|
|
|
ImGui::TableSetColumnIndex(2);
|
|
switch (g_config.net.nat.forward_ports[row].protocol) {
|
|
case CONFIG_NET_NAT_FORWARD_PORTS_PROTOCOL_TCP:
|
|
ImGui::TextUnformatted("TCP"); break;
|
|
case CONFIG_NET_NAT_FORWARD_PORTS_PROTOCOL_UDP:
|
|
ImGui::TextUnformatted("UDP"); break;
|
|
default: assert(0);
|
|
}
|
|
|
|
ImGui::TableSetColumnIndex(3);
|
|
ImGui::PushID(row);
|
|
if (ImGui::Button("Remove")) {
|
|
remove_net_nat_forward_ports(row);
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::TableNextRow();
|
|
|
|
ImGui::TableSetColumnIndex(0);
|
|
static char buf[8] = {"1234"};
|
|
ImGui::SetNextItemWidth(ImGui::GetColumnWidth());
|
|
ImGui::InputText("###hostport", buf, sizeof(buf));
|
|
|
|
ImGui::TableSetColumnIndex(1);
|
|
static char buf2[8] = {"1234"};
|
|
ImGui::SetNextItemWidth(ImGui::GetColumnWidth());
|
|
ImGui::InputText("###guestport", buf2, sizeof(buf2));
|
|
|
|
ImGui::TableSetColumnIndex(2);
|
|
static CONFIG_NET_NAT_FORWARD_PORTS_PROTOCOL protocol =
|
|
CONFIG_NET_NAT_FORWARD_PORTS_PROTOCOL_TCP;
|
|
assert(sizeof(protocol) >= sizeof(int));
|
|
ImGui::SetNextItemWidth(ImGui::GetColumnWidth());
|
|
ImGui::Combo("###protocol", &protocol, "TCP\0UDP\0");
|
|
|
|
ImGui::TableSetColumnIndex(3);
|
|
if (ImGui::Button("Add")) {
|
|
int host, guest;
|
|
if (sscanf(buf, "%d", &host) == 1 &&
|
|
sscanf(buf2, "%d", &guest) == 1) {
|
|
add_net_nat_forward_ports(host, guest, protocol);
|
|
}
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::PopStyleVar();
|
|
}
|
|
|
|
void MainMenuNetworkView::DrawUdpOptions(bool appearing)
|
|
{
|
|
if (appearing) {
|
|
strncpy(remote_addr, g_config.net.udp.remote_addr, sizeof(remote_addr)-1);
|
|
strncpy(local_addr, g_config.net.udp.bind_addr, sizeof(local_addr)-1);
|
|
}
|
|
|
|
float size_ratio = 0.5;
|
|
float width = ImGui::GetColumnWidth() * size_ratio;
|
|
ImGui::PushFont(g_font_mgr.m_menu_font_small);
|
|
PrepareComboTitleDescription(
|
|
"Remote Address",
|
|
"Destination addr:port to forward packets to (1.2.3.4:9968)",
|
|
size_ratio);
|
|
ImGui::SetNextItemWidth(width);
|
|
if (ImGui::InputText("###remote_host", remote_addr, sizeof(remote_addr))) {
|
|
xemu_settings_set_string(&g_config.net.udp.remote_addr, remote_addr);
|
|
}
|
|
PrepareComboTitleDescription(
|
|
"Bind Address", "Local addr:port to receive packets on (0.0.0.0:9968)",
|
|
size_ratio);
|
|
ImGui::SetNextItemWidth(width);
|
|
if (ImGui::InputText("###local_host", local_addr, sizeof(local_addr))) {
|
|
xemu_settings_set_string(&g_config.net.udp.bind_addr, local_addr);
|
|
}
|
|
ImGui::PopFont();
|
|
}
|
|
|
|
#if 0
|
|
class MainMenuSnapshotsView : public virtual MainMenuTabView
|
|
{
|
|
protected:
|
|
GLuint screenshot;
|
|
|
|
public:
|
|
void initScreenshot()
|
|
{
|
|
if (screenshot == 0) {
|
|
glGenTextures(1, &screenshot);
|
|
int w, h, n;
|
|
stbi_set_flip_vertically_on_load(0);
|
|
unsigned char *data = stbi_load("./data/screenshot.png", &w, &h, &n, 4);
|
|
assert(n == 4);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, screenshot);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
|
stbi_image_free(data);
|
|
}
|
|
}
|
|
|
|
void snapshotBigButton(const char *name, const char *title_name, GLuint screenshot)
|
|
{
|
|
ImGuiStyle &style = ImGui::GetStyle();
|
|
ImVec2 pos = ImGui::GetCursorPos();
|
|
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
|
|
|
ImGui::PushFont(g_font_mgr.m_menuFont);
|
|
const char *icon = ICON_FA_CIRCLE_XMARK;
|
|
ImVec2 ts_icon = ImGui::CalcTextSize(icon);
|
|
ts_icon.x += 2*style.FramePadding.x;
|
|
ImGui::PopFont();
|
|
|
|
ImGui::PushFont(g_font_mgr.m_menuFontSmall);
|
|
ImVec2 ts_sub = ImGui::CalcTextSize(name);
|
|
ImGui::PopFont();
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, g_viewport_mgr.scale(ImVec2(5, 5)));
|
|
ImGui::PushFont(g_font_mgr.m_menuFontMedium);
|
|
|
|
ImVec2 ts_title = ImGui::CalcTextSize(name);
|
|
ImVec2 thumbnail_size = g_viewport_mgr.scale(ImVec2(160, 120));
|
|
ImVec2 thumbnail_pos(style.FramePadding.x, style.FramePadding.y);
|
|
ImVec2 text_pos(thumbnail_pos.x + thumbnail_size.x + style.FramePadding.x * 2, thumbnail_pos.y);
|
|
ImVec2 subtext_pos(text_pos.x, text_pos.y + ts_title.y + style.FramePadding.x);
|
|
|
|
ImGui::Button("###button", ImVec2(ImGui::GetContentRegionAvail().x, fmax(thumbnail_size.y + style.FramePadding.y * 2,
|
|
ts_title.y + ts_sub.y + style.FramePadding.y * 3)));
|
|
ImGui::PopFont();
|
|
const ImVec2 sz = ImGui::GetItemRectSize();
|
|
const ImVec2 p0 = ImGui::GetItemRectMin();
|
|
const ImVec2 p1 = ImGui::GetItemRectMax();
|
|
ts_icon.y = sz.y;
|
|
|
|
// Snapshot thumbnail
|
|
ImGui::SetItemAllowOverlap();
|
|
ImGui::SameLine();
|
|
ImGui::SetCursorPosX(pos.x + thumbnail_pos.x);
|
|
ImGui::SetCursorPosY(pos.y + thumbnail_pos.y);
|
|
ImGui::Image((ImTextureID)screenshot, thumbnail_size, ImVec2(0,0), ImVec2(1,1));
|
|
|
|
draw_list->PushClipRect(p0, p1, true);
|
|
|
|
// Snapshot title
|
|
ImGui::PushFont(g_font_mgr.m_menuFontMedium);
|
|
draw_list->AddText(ImVec2(p0.x + text_pos.x, p0.y + text_pos.y), IM_COL32(255, 255, 255, 255), name);
|
|
ImGui::PopFont();
|
|
|
|
// Snapshot subtitle
|
|
ImGui::PushFont(g_font_mgr.m_menuFontSmall);
|
|
draw_list->AddText(ImVec2(p0.x + subtext_pos.x, p0.y + subtext_pos.y), IM_COL32(255, 255, 255, 200), title_name);
|
|
ImGui::PopFont();
|
|
|
|
draw_list->PopClipRect();
|
|
|
|
// Delete button
|
|
ImGui::SameLine();
|
|
ImGui::SetCursorPosY(pos.y);
|
|
ImGui::SetCursorPosX(pos.x + sz.x - ts_icon.x);
|
|
ImGui::PushFont(g_font_mgr.m_menuFont);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0));
|
|
ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32_BLACK_TRANS);
|
|
ImGui::Button(icon, ts_icon);
|
|
ImGui::PopStyleColor();
|
|
ImGui::PopStyleVar(1);
|
|
ImGui::PopFont();
|
|
ImGui::PopStyleVar(2);
|
|
}
|
|
|
|
void Draw()
|
|
{
|
|
initScreenshot();
|
|
for (int i = 0; i < 15; i++) {
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "%s", "Apr 9 2022 19:44");
|
|
ImGui::PushID(i);
|
|
snapshotBigButton(buf, "Halo: Combat Evolved", screenshot);
|
|
ImGui::PopID();
|
|
}
|
|
}
|
|
};
|
|
#endif
|
|
|
|
MainMenuSystemView::MainMenuSystemView() : m_dirty(false)
|
|
{
|
|
}
|
|
|
|
void MainMenuSystemView::Draw()
|
|
{
|
|
const char *rom_file_filters = ".bin Files\0*.bin\0.rom Files\0*.rom\0All Files\0*.*\0";
|
|
const char *qcow_file_filters = ".qcow2 Files\0*.qcow2\0All Files\0*.*\0";
|
|
|
|
if (m_dirty) {
|
|
ImGui::TextColored(ImVec4(1,0,0,1), "Application restart required to apply settings");
|
|
}
|
|
|
|
SectionTitle("System Configuration");
|
|
|
|
if (ChevronCombo(
|
|
"System Memory", &g_config.sys.mem_limit,
|
|
"64 MiB (Default)\0""128 MiB\0",
|
|
"Increase to 128 MiB for debug or homebrew applications")) {
|
|
m_dirty = true;
|
|
}
|
|
|
|
if (ChevronCombo(
|
|
"AV Pack", &g_config.sys.avpack,
|
|
"SCART\0HDTV (Default)\0VGA\0RFU\0S-Video\0Composite\0None\0",
|
|
"Select the attached AV pack")) {
|
|
m_dirty = true;
|
|
}
|
|
|
|
SectionTitle("Files");
|
|
if (FilePicker("Boot ROM", &g_config.sys.files.bootrom_path,
|
|
rom_file_filters)) {
|
|
m_dirty = true;
|
|
}
|
|
if (FilePicker("Flash ROM", &g_config.sys.files.flashrom_path,
|
|
rom_file_filters)) {
|
|
m_dirty = true;
|
|
}
|
|
if (FilePicker("Hard Disk", &g_config.sys.files.hdd_path,
|
|
qcow_file_filters)) {
|
|
m_dirty = true;
|
|
}
|
|
if (FilePicker("EEPROM", &g_config.sys.files.eeprom_path,
|
|
rom_file_filters)) {
|
|
m_dirty = true;
|
|
}
|
|
}
|
|
|
|
void MainMenuAboutView::Draw()
|
|
{
|
|
static const char *build_info_text = NULL;
|
|
if (build_info_text == NULL) {
|
|
build_info_text = g_strdup_printf(
|
|
"Version: %s\nBranch: %s\nCommit: %s\nDate: %s",
|
|
xemu_version, xemu_branch, xemu_commit, xemu_date);
|
|
}
|
|
|
|
static const char *sys_info_text = NULL;
|
|
if (sys_info_text == NULL) {
|
|
const char *gl_shader_version = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
|
|
const char *gl_version = (const char*)glGetString(GL_VERSION);
|
|
const char *gl_renderer = (const char*)glGetString(GL_RENDERER);
|
|
const char *gl_vendor = (const char*)glGetString(GL_VENDOR);
|
|
sys_info_text = g_strdup_printf(
|
|
"CPU: %s\nOS Platform: %s\nOS Version: %s\nManufacturer: %s\n"
|
|
"GPU Model: %s\nDriver: %s\nShader: %s",
|
|
xemu_get_cpu_info(), xemu_get_os_platform(), xemu_get_os_info(), gl_vendor,
|
|
gl_renderer, gl_version, gl_shader_version);
|
|
}
|
|
|
|
static uint32_t time_start = 0;
|
|
if (ImGui::IsWindowAppearing()) {
|
|
time_start = SDL_GetTicks();
|
|
}
|
|
uint32_t now = SDL_GetTicks() - time_start;
|
|
|
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY()-50*g_viewport_mgr.m_scale);
|
|
ImGui::SetCursorPosX((ImGui::GetWindowWidth()-256*g_viewport_mgr.m_scale)/2);
|
|
|
|
logo_fbo->Target();
|
|
ImTextureID id = (ImTextureID)(intptr_t)logo_fbo->Texture();
|
|
float t_w = 256.0;
|
|
float t_h = 256.0;
|
|
float x_off = 0;
|
|
ImGui::Image(id,
|
|
ImVec2((t_w-x_off)*g_viewport_mgr.m_scale, t_h*g_viewport_mgr.m_scale),
|
|
ImVec2(x_off/t_w, t_h/t_h),
|
|
ImVec2(t_w/t_w, 0));
|
|
if (ImGui::IsItemClicked()) {
|
|
time_start = SDL_GetTicks();
|
|
}
|
|
RenderLogo(now, 0x42e335ff, 0x42e335ff, 0x00000000);
|
|
logo_fbo->Restore();
|
|
|
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY()-75*g_viewport_mgr.m_scale);
|
|
|
|
SectionTitle("Build Information");
|
|
ImGui::PushFont(g_font_mgr.m_fixed_width_font);
|
|
ImGui::InputTextMultiline("##build_info", (char *)build_info_text,
|
|
strlen(build_info_text),
|
|
ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 5),
|
|
ImGuiInputTextFlags_ReadOnly);
|
|
ImGui::PopFont();
|
|
|
|
SectionTitle("System Information");
|
|
ImGui::PushFont(g_font_mgr.m_fixed_width_font);
|
|
ImGui::InputTextMultiline("###systeminformation", (char *)sys_info_text,
|
|
strlen(sys_info_text),
|
|
ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 8),
|
|
ImGuiInputTextFlags_ReadOnly);
|
|
ImGui::PopFont();
|
|
|
|
SectionTitle("Community");
|
|
|
|
ImGui::Text("Visit");
|
|
ImGui::SameLine();
|
|
if (ImGui::SmallButton("https://xemu.app")) {
|
|
xemu_open_web_browser("https://xemu.app");
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::Text("for more information");
|
|
}
|
|
|
|
MainMenuTabButton::MainMenuTabButton(std::string text, std::string icon)
|
|
: m_icon(icon), m_text(text)
|
|
{
|
|
}
|
|
|
|
bool MainMenuTabButton::Draw(bool selected)
|
|
{
|
|
ImGuiStyle &style = ImGui::GetStyle();
|
|
|
|
ImU32 col = selected ?
|
|
ImGui::GetColorU32(style.Colors[ImGuiCol_ButtonHovered]) :
|
|
IM_COL32(0, 0, 0, 0);
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_Button, col);
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, selected ? col : IM_COL32(32, 32, 32, 255));
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, selected ? col : IM_COL32(32, 32, 32, 255));
|
|
int p = ImGui::GetTextLineHeight() * 0.5;
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(p, p));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0.5));
|
|
ImGui::PushFont(g_font_mgr.m_menu_font);
|
|
|
|
ImVec2 button_size = ImVec2(-FLT_MIN, 0);
|
|
auto text = string_format("%s %s", m_icon.c_str(), m_text.c_str());
|
|
ImGui::PushID(this);
|
|
bool status = ImGui::Button(text.c_str(), button_size);
|
|
ImGui::PopID();
|
|
ImGui::PopFont();
|
|
ImGui::PopStyleVar(3);
|
|
ImGui::PopStyleColor(3);
|
|
return status;
|
|
}
|
|
|
|
MainMenuScene::MainMenuScene()
|
|
: m_animation(0.12, 0.12),
|
|
m_general_button("General", ICON_FA_GEARS),
|
|
m_input_button("Input", ICON_FA_GAMEPAD),
|
|
m_display_button("Display", ICON_FA_TV),
|
|
m_audio_button("Audio", ICON_FA_VOLUME_HIGH),
|
|
m_network_button("Network", ICON_FA_NETWORK_WIRED),
|
|
// m_snapshots_button("Snapshots", ICON_FA_CLOCK_ROTATE_LEFT),
|
|
m_system_button("System", ICON_FA_MICROCHIP),
|
|
m_about_button("About", ICON_FA_CIRCLE_INFO)
|
|
{
|
|
m_had_focus_last_frame = false;
|
|
m_focus_view = false;
|
|
m_tabs.push_back(&m_general_button);
|
|
m_tabs.push_back(&m_input_button);
|
|
m_tabs.push_back(&m_display_button);
|
|
m_tabs.push_back(&m_audio_button);
|
|
m_tabs.push_back(&m_network_button);
|
|
// m_tabs.push_back(&m_snapshots_button);
|
|
m_tabs.push_back(&m_system_button);
|
|
m_tabs.push_back(&m_about_button);
|
|
|
|
m_views.push_back(&m_general_view);
|
|
m_views.push_back(&m_input_view);
|
|
m_views.push_back(&m_display_view);
|
|
m_views.push_back(&m_audio_view);
|
|
m_views.push_back(&m_network_view);
|
|
// m_views.push_back(&m_snapshots_view);
|
|
m_views.push_back(&m_system_view);
|
|
m_views.push_back(&m_about_view);
|
|
|
|
m_current_view_index = 0;
|
|
m_next_view_index = m_current_view_index;
|
|
}
|
|
|
|
void MainMenuScene::ShowGeneral()
|
|
{
|
|
SetNextViewIndexWithFocus(0);
|
|
}
|
|
void MainMenuScene::ShowInput()
|
|
{
|
|
SetNextViewIndexWithFocus(1);
|
|
}
|
|
void MainMenuScene::ShowDisplay()
|
|
{
|
|
SetNextViewIndexWithFocus(2);
|
|
}
|
|
void MainMenuScene::ShowAudio()
|
|
{
|
|
SetNextViewIndexWithFocus(3);
|
|
}
|
|
void MainMenuScene::ShowNetwork()
|
|
{
|
|
SetNextViewIndexWithFocus(4);
|
|
}
|
|
// void MainMenuScene::showSnapshots() { SetNextViewIndexWithFocus(5); }
|
|
void MainMenuScene::ShowSystem()
|
|
{
|
|
SetNextViewIndexWithFocus(5);
|
|
}
|
|
void MainMenuScene::ShowAbout()
|
|
{
|
|
SetNextViewIndexWithFocus(6);
|
|
}
|
|
|
|
void MainMenuScene::SetNextViewIndexWithFocus(int i)
|
|
{
|
|
m_focus_view = true;
|
|
SetNextViewIndex(i);
|
|
|
|
if (!g_scene_mgr.IsDisplayingScene()) {
|
|
g_scene_mgr.PushScene(*this);
|
|
}
|
|
}
|
|
|
|
void MainMenuScene::Show()
|
|
{
|
|
m_background.Show();
|
|
m_nav_control_view.Show();
|
|
m_animation.EaseIn();
|
|
}
|
|
|
|
void MainMenuScene::Hide()
|
|
{
|
|
m_background.Hide();
|
|
m_nav_control_view.Hide();
|
|
m_animation.EaseOut();
|
|
}
|
|
|
|
bool MainMenuScene::IsAnimating()
|
|
{
|
|
return m_animation.IsAnimating();
|
|
}
|
|
|
|
void MainMenuScene::SetNextViewIndex(int i)
|
|
{
|
|
m_next_view_index = i % m_tabs.size();
|
|
g_config.general.last_viewed_menu_index = i;
|
|
}
|
|
|
|
void MainMenuScene::HandleInput()
|
|
{
|
|
bool nofocus = !ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow);
|
|
bool focus = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows |
|
|
ImGuiFocusedFlags_NoPopupHierarchy);
|
|
|
|
// XXX: Ensure we have focus for two frames. If a user cancels a popup window, we do not want to cancel main
|
|
// window as well.
|
|
if (nofocus || (focus && m_had_focus_last_frame &&
|
|
ImGui::IsNavInputTest(ImGuiNavInput_Cancel,
|
|
ImGuiInputReadMode_Pressed))) {
|
|
Hide();
|
|
return;
|
|
}
|
|
|
|
if (focus && m_had_focus_last_frame) {
|
|
if (ImGui::IsKeyPressed(ImGuiKey_GamepadL1)) {
|
|
SetNextViewIndex((m_current_view_index + m_tabs.size() - 1) %
|
|
m_tabs.size());
|
|
}
|
|
|
|
if (ImGui::IsKeyPressed(ImGuiKey_GamepadR1)) {
|
|
SetNextViewIndex((m_current_view_index + 1) % m_tabs.size());
|
|
}
|
|
}
|
|
|
|
m_had_focus_last_frame = focus;
|
|
}
|
|
|
|
bool MainMenuScene::Draw()
|
|
{
|
|
m_animation.Step();
|
|
m_background.Draw();
|
|
m_nav_control_view.Draw();
|
|
|
|
ImGuiIO &io = ImGui::GetIO();
|
|
float t = m_animation.GetSinInterpolatedValue();
|
|
float window_alpha = t;
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, window_alpha);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0);
|
|
|
|
ImVec4 extents = g_viewport_mgr.GetExtents();
|
|
ImVec2 window_pos = ImVec2(io.DisplaySize.x / 2, extents.y);
|
|
ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, ImVec2(0.5, 0));
|
|
|
|
ImVec2 max_size = g_viewport_mgr.Scale(ImVec2(800, 0));
|
|
float x = fmin(io.DisplaySize.x - extents.x - extents.z, max_size.x);
|
|
float y = io.DisplaySize.y - extents.y - extents.w;
|
|
ImGui::SetNextWindowSize(ImVec2(x, y));
|
|
|
|
if (ImGui::Begin("###MainWindow", NULL,
|
|
ImGuiWindowFlags_NoDecoration |
|
|
ImGuiWindowFlags_NoSavedSettings)) {
|
|
//
|
|
// Nav menu
|
|
//
|
|
|
|
float width = ImGui::GetWindowWidth();
|
|
float nav_width = width * 0.3;
|
|
float content_width = width - nav_width;
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(26,26,26,255));
|
|
|
|
ImGui::BeginChild("###MainWindowNav", ImVec2(nav_width, -1), true, ImGuiWindowFlags_NavFlattened);
|
|
|
|
bool move_focus_to_tab = false;
|
|
if (m_current_view_index != m_next_view_index) {
|
|
m_current_view_index = m_next_view_index;
|
|
if (!m_focus_view) {
|
|
move_focus_to_tab = true;
|
|
}
|
|
}
|
|
|
|
int i = 0;
|
|
for (auto &button : m_tabs) {
|
|
if (move_focus_to_tab && i == m_current_view_index) {
|
|
ImGui::SetKeyboardFocusHere();
|
|
move_focus_to_tab = false;
|
|
}
|
|
if (button->Draw(i == m_current_view_index)) {
|
|
SetNextViewIndex(i);
|
|
}
|
|
if (i == m_current_view_index) {
|
|
ImGui::SetItemDefaultFocus();
|
|
}
|
|
i++;
|
|
}
|
|
ImGui::EndChild();
|
|
ImGui::PopStyleColor();
|
|
|
|
//
|
|
// Content
|
|
//
|
|
ImGui::SameLine();
|
|
int s = ImGui::GetTextLineHeight() * 0.75;
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(s, s));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(s, s));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 6*g_viewport_mgr.m_scale);
|
|
|
|
ImGui::PushID(m_current_view_index);
|
|
ImGui::BeginChild("###MainWindowContent", ImVec2(content_width, -1),
|
|
true,
|
|
ImGuiWindowFlags_AlwaysUseWindowPadding |
|
|
ImGuiWindowFlags_NavFlattened);
|
|
|
|
if (!g_input_mgr.IsNavigatingWithController()) {
|
|
// Close button
|
|
ImGui::PushFont(g_font_mgr.m_menu_font);
|
|
ImGuiStyle &style = ImGui::GetStyle();
|
|
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 128));
|
|
ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32_BLACK_TRANS);
|
|
ImVec2 pos = ImGui::GetCursorPos();
|
|
ImGui::SetCursorPosX(ImGui::GetContentRegionMax().x - style.FramePadding.x * 2.0f - ImGui::GetTextLineHeight());
|
|
if (ImGui::Button(ICON_FA_XMARK)) {
|
|
Hide();
|
|
}
|
|
ImGui::SetCursorPos(pos);
|
|
ImGui::PopStyleColor(2);
|
|
ImGui::PopFont();
|
|
}
|
|
|
|
ImGui::PushFont(g_font_mgr.m_default_font);
|
|
if (m_focus_view) {
|
|
ImGui::SetKeyboardFocusHere();
|
|
m_focus_view = false;
|
|
}
|
|
m_views[m_current_view_index]->Draw();
|
|
|
|
ImGui::PopFont();
|
|
ImGui::EndChild();
|
|
ImGui::PopID();
|
|
ImGui::PopStyleVar(3);
|
|
|
|
HandleInput();
|
|
}
|
|
ImGui::End();
|
|
ImGui::PopStyleVar(5);
|
|
|
|
return !m_animation.IsComplete();
|
|
}
|