diff --git a/Externals/wxWidgets3/src/generic/tipwin.cpp b/Externals/wxWidgets3/src/generic/tipwin.cpp index abe66b3275..b022ac3c4f 100644 --- a/Externals/wxWidgets3/src/generic/tipwin.cpp +++ b/Externals/wxWidgets3/src/generic/tipwin.cpp @@ -218,6 +218,9 @@ void wxTipWindow::Close() *m_windowPtr = NULL; m_windowPtr = NULL; } + // XXX: Dolphin: Prevents an assertion failure due to Close being called multiple times. + if (!IsShown()) + return; #if wxUSE_POPUPWIN Show(false); diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index b6b74e5160..86cc0b4b1e 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <cinttypes> +#include <climits> #include <memory> #include "Common/CDUtils.h" @@ -138,8 +139,8 @@ void SConfig::SaveInterfaceSettings(IniFile& ini) interface->Set("OnScreenDisplayMessages", bOnScreenDisplayMessages); interface->Set("HideCursor", bHideCursor); interface->Set("AutoHideCursor", bAutoHideCursor); - interface->Set("MainWindowPosX", (iPosX == -32000) ? 0 : iPosX); // TODO - HAX - interface->Set("MainWindowPosY", (iPosY == -32000) ? 0 : iPosY); // TODO - HAX + interface->Set("MainWindowPosX", iPosX); + interface->Set("MainWindowPosY", iPosY); interface->Set("MainWindowWidth", iWidth); interface->Set("MainWindowHeight", iHeight); interface->Set("LanguageCode", m_InterfaceLanguage); @@ -402,10 +403,10 @@ void SConfig::LoadInterfaceSettings(IniFile& ini) interface->Get("OnScreenDisplayMessages", &bOnScreenDisplayMessages, true); interface->Get("HideCursor", &bHideCursor, false); interface->Get("AutoHideCursor", &bAutoHideCursor, false); - interface->Get("MainWindowPosX", &iPosX, 100); - interface->Get("MainWindowPosY", &iPosY, 100); - interface->Get("MainWindowWidth", &iWidth, 800); - interface->Get("MainWindowHeight", &iHeight, 600); + interface->Get("MainWindowPosX", &iPosX, INT_MIN); + interface->Get("MainWindowPosY", &iPosY, INT_MIN); + interface->Get("MainWindowWidth", &iWidth, -1); + interface->Get("MainWindowHeight", &iHeight, -1); interface->Get("LanguageCode", &m_InterfaceLanguage, ""); interface->Get("ShowToolbar", &m_InterfaceToolbar, true); interface->Get("ShowStatusbar", &m_InterfaceStatusbar, true); @@ -663,10 +664,10 @@ void SConfig::LoadDefaults() bDPL2Decoder = false; iLatency = 14; - iPosX = 100; - iPosY = 100; - iWidth = 800; - iHeight = 600; + iPosX = INT_MIN; + iPosY = INT_MIN; + iWidth = -1; + iHeight = -1; m_analytics_id = ""; m_analytics_enabled = false; diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index f2275bb4c4..5ea7a58386 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -4,6 +4,7 @@ #pragma once +#include <limits> #include <string> #include <vector> @@ -129,8 +130,10 @@ struct SConfig : NonCopyable // Display settings std::string strFullscreenResolution; - int iRenderWindowXPos = -1, iRenderWindowYPos = -1; - int iRenderWindowWidth = 640, iRenderWindowHeight = 480; + int iRenderWindowXPos = std::numeric_limits<int>::min(); + int iRenderWindowYPos = std::numeric_limits<int>::min(); + int iRenderWindowWidth = -1; + int iRenderWindowHeight = -1; bool bRenderWindowAutoSize = false, bKeepWindowOnTop = false; bool bFullscreen = false, bRenderToMain = false; bool bProgressive = false, bPAL60 = false; diff --git a/Source/Core/DolphinWX/AboutDolphin.cpp b/Source/Core/DolphinWX/AboutDolphin.cpp index 90702ae34e..9fb555c4a9 100644 --- a/Source/Core/DolphinWX/AboutDolphin.cpp +++ b/Source/Core/DolphinWX/AboutDolphin.cpp @@ -20,8 +20,8 @@ AboutDolphin::AboutDolphin(wxWindow* parent, wxWindowID id, const wxString& titl const wxPoint& position, const wxSize& size, long style) : wxDialog(parent, id, title, position, size, style) { - wxGenericStaticBitmap* const sbDolphinLogo = - new wxGenericStaticBitmap(this, wxID_ANY, WxUtils::LoadResourceBitmap("dolphin_logo")); + wxGenericStaticBitmap* const sbDolphinLogo = new wxGenericStaticBitmap( + this, wxID_ANY, WxUtils::LoadScaledResourceBitmap("dolphin_logo", this)); const wxString DolphinText = _("Dolphin"); const wxString RevisionText = scm_desc_str; @@ -80,6 +80,12 @@ AboutDolphin::AboutDolphin(wxWindow* parent, wxWindowID id, const wxString& titl wxSizerFlags center_flag; center_flag.Center(); + const int space5 = FromDIP(5); + const int space10 = FromDIP(10); + const int space15 = FromDIP(15); + const int space30 = FromDIP(30); + const int space40 = FromDIP(40); + const int space75 = FromDIP(75); wxBoxSizer* const sCheckUpdates = new wxBoxSizer(wxHORIZONTAL); sCheckUpdates->Add(UpdateText, center_flag); @@ -94,30 +100,30 @@ AboutDolphin::AboutDolphin(wxWindow* parent, wxWindowID id, const wxString& titl wxBoxSizer* const sInfo = new wxBoxSizer(wxVERTICAL); sInfo->Add(Dolphin); - sInfo->AddSpacer(5); + sInfo->AddSpacer(space5); sInfo->Add(Revision); - sInfo->AddSpacer(10); + sInfo->AddSpacer(space10); sInfo->Add(Branch); sInfo->Add(sCheckUpdates); sInfo->Add(Message); sInfo->Add(sLinks); wxBoxSizer* const sLogo = new wxBoxSizer(wxVERTICAL); - sLogo->AddSpacer(75); + sLogo->AddSpacer(space75); sLogo->Add(sbDolphinLogo); - sLogo->AddSpacer(40); + sLogo->AddSpacer(space40); wxBoxSizer* const sMainHor = new wxBoxSizer(wxHORIZONTAL); - sMainHor->AddSpacer(30); + sMainHor->AddSpacer(space30); sMainHor->Add(sLogo); - sMainHor->AddSpacer(30); + sMainHor->AddSpacer(space30); sMainHor->Add(sInfo); - sMainHor->AddSpacer(30); + sMainHor->AddSpacer(space30); wxBoxSizer* const sFooter = new wxBoxSizer(wxVERTICAL); - sFooter->AddSpacer(15); + sFooter->AddSpacer(space15); sFooter->Add(Copyright, 0, wxALIGN_CENTER_HORIZONTAL); - sFooter->AddSpacer(5); + sFooter->AddSpacer(space5); wxBoxSizer* const sMain = new wxBoxSizer(wxVERTICAL); sMain->Add(sMainHor, 1, wxEXPAND); diff --git a/Source/Core/DolphinWX/CMakeLists.txt b/Source/Core/DolphinWX/CMakeLists.txt index 9a01978bbc..27d7c73385 100644 --- a/Source/Core/DolphinWX/CMakeLists.txt +++ b/Source/Core/DolphinWX/CMakeLists.txt @@ -40,6 +40,7 @@ set(GUI_SRCS NetPlay/NetPlaySetupFrame.cpp NetPlay/NetWindow.cpp NetPlay/PadMapDialog.cpp + DolphinSlider.cpp FifoPlayerDlg.cpp Frame.cpp FrameAui.cpp diff --git a/Source/Core/DolphinWX/Cheats/CheatSearchTab.cpp b/Source/Core/DolphinWX/Cheats/CheatSearchTab.cpp index 25ad5860e5..494f818c3f 100644 --- a/Source/Core/DolphinWX/Cheats/CheatSearchTab.cpp +++ b/Source/Core/DolphinWX/Cheats/CheatSearchTab.cpp @@ -27,10 +27,7 @@ #include "DolphinWX/Cheats/CreateCodeDialog.h" #include "DolphinWX/WxUtils.h" -namespace -{ -const unsigned int MAX_CHEAT_SEARCH_RESULTS_DISPLAY = 1024; -} +static constexpr unsigned int MAX_CHEAT_SEARCH_RESULTS_DISPLAY = 1024; CheatSearchTab::CheatSearchTab(wxWindow* const parent) : wxPanel(parent) { @@ -39,13 +36,25 @@ CheatSearchTab::CheatSearchTab(wxWindow* const parent) : wxPanel(parent) // first scan button m_btn_init_scan = new wxButton(this, wxID_ANY, _("New Scan")); + m_btn_init_scan->SetToolTip(_("Perform a full index of the game's RAM at the current Data Size. " + "Required before any Searching can be performed.")); m_btn_init_scan->Bind(wxEVT_BUTTON, &CheatSearchTab::OnNewScanClicked, this); + m_btn_init_scan->Disable(); // next scan button m_btn_next_scan = new wxButton(this, wxID_ANY, _("Next Scan")); + m_btn_next_scan->SetToolTip(_("Eliminate items from the current scan results that do not match " + "the current Search settings.")); m_btn_next_scan->Bind(wxEVT_BUTTON, &CheatSearchTab::OnNextScanClicked, this); m_btn_next_scan->Disable(); + m_label_scan_disabled = new wxStaticText(this, wxID_ANY, _("No game is running.")); + + // create AR code button + m_btn_create_code = new wxButton(this, wxID_ANY, _("Create AR Code")); + m_btn_create_code->Bind(wxEVT_BUTTON, &CheatSearchTab::OnCreateARCodeClicked, this); + m_btn_create_code->Disable(); + // data sizes radiobox std::array<wxString, 3> data_size_names = {{_("8-bit"), _("16-bit"), _("32-bit")}}; m_data_sizes = new wxRadioBox(this, wxID_ANY, _("Data Size"), wxDefaultPosition, wxDefaultSize, @@ -54,30 +63,39 @@ CheatSearchTab::CheatSearchTab(wxWindow* const parent) : wxPanel(parent) // ListView for search results m_lview_search_results = new wxListView(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL); - ResetListViewColumns(); + m_lview_search_results->AppendColumn(_("Address")); + m_lview_search_results->AppendColumn(_("Value")); + m_lview_search_results->AppendColumn(_("Value (float)")); + m_lview_search_results->AppendColumn(_("Value (double)")); + m_lview_search_results->Bind(wxEVT_LIST_ITEM_ACTIVATED, &CheatSearchTab::OnListViewItemActivated, + this); + m_lview_search_results->Bind(wxEVT_LIST_ITEM_SELECTED, &CheatSearchTab::OnListViewItemSelected, + this); + m_lview_search_results->Bind(wxEVT_LIST_ITEM_DESELECTED, &CheatSearchTab::OnListViewItemSelected, + this); // Result count m_label_results_count = new wxStaticText(this, wxID_ANY, _("Count:")); - // create AR code button - wxButton* const button_cheat_search_copy_address = - new wxButton(this, wxID_ANY, _("Create AR Code")); - button_cheat_search_copy_address->Bind(wxEVT_BUTTON, &CheatSearchTab::OnCreateARCodeClicked, - this); + const int space5 = FromDIP(5); // results groupbox wxStaticBoxSizer* const sizer_cheat_search_results = new wxStaticBoxSizer(wxVERTICAL, this, _("Results")); - sizer_cheat_search_results->Add(m_label_results_count, 0, wxALIGN_LEFT | wxALL, 5); - sizer_cheat_search_results->Add(m_lview_search_results, 1, wxEXPAND | wxALL, 5); - sizer_cheat_search_results->Add(button_cheat_search_copy_address, 0, - wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 5); + sizer_cheat_search_results->AddSpacer(space5); + sizer_cheat_search_results->Add(m_label_results_count, 0, wxLEFT | wxRIGHT, space5); + sizer_cheat_search_results->AddSpacer(space5); + sizer_cheat_search_results->Add(m_lview_search_results, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + sizer_cheat_search_results->AddSpacer(space5); + sizer_cheat_search_results->Add(m_btn_create_code, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sizer_cheat_search_results->AddSpacer(space5); // search value textbox - m_textctrl_value_x = new wxTextCtrl(this, wxID_ANY, "0x0", wxDefaultPosition, wxSize(96, -1)); - - wxBoxSizer* const sizer_cheat_filter_text = new wxBoxSizer(wxHORIZONTAL); - sizer_cheat_filter_text->Add(m_textctrl_value_x, 1, wxALIGN_CENTER_VERTICAL, 5); + m_textctrl_value_x = new wxTextCtrl(this, wxID_ANY, "0x0"); + m_textctrl_value_x->SetMinSize(WxUtils::GetTextWidgetMinSize(m_textctrl_value_x, "0x00000000 ")); + m_textctrl_value_x->SetToolTip( + _("Value to match against. Can be Hex (\"0x\"), Octal (\"0\") or Decimal. " + "Leave blank to filter each result against its own previous value.")); // Filter types in the compare dropdown // TODO: Implement between search @@ -93,33 +111,46 @@ CheatSearchTab::CheatSearchTab(wxWindow* const parent) : wxPanel(parent) wxStaticBoxSizer* const sizer_cheat_search_filter = new wxStaticBoxSizer(wxVERTICAL, this, _("Search (clear to use previous value)")); - sizer_cheat_search_filter->Add(sizer_cheat_filter_text, 0, wxALL | wxEXPAND, 5); - sizer_cheat_search_filter->Add(m_search_type, 0, wxALL, 5); - - // left sizer - wxBoxSizer* const sizer_left = new wxBoxSizer(wxVERTICAL); - sizer_left->Add(sizer_cheat_search_results, 1, wxEXPAND, 5); + sizer_cheat_search_filter->AddSpacer(space5); + sizer_cheat_search_filter->Add(m_textctrl_value_x, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sizer_cheat_search_filter->AddSpacer(space5); + sizer_cheat_search_filter->Add(m_search_type, 0, wxLEFT | wxRIGHT, space5); + sizer_cheat_search_filter->AddSpacer(space5); // button sizer wxBoxSizer* boxButtons = new wxBoxSizer(wxHORIZONTAL); - boxButtons->Add(m_btn_init_scan, 1, wxRIGHT, 5); - boxButtons->Add(m_btn_next_scan, 1); + boxButtons->Add(m_btn_init_scan, 1); + boxButtons->Add(m_btn_next_scan, 1, wxLEFT, space5); // right sizer wxBoxSizer* const sizer_right = new wxBoxSizer(wxVERTICAL); - sizer_right->Add(m_data_sizes, 0, wxEXPAND | wxBOTTOM, 5); - sizer_right->Add(sizer_cheat_search_filter, 0, wxEXPAND | wxBOTTOM, 5); + sizer_right->Add(m_data_sizes, 0, wxEXPAND); + sizer_right->Add(sizer_cheat_search_filter, 0, wxEXPAND | wxTOP, space5); sizer_right->AddStretchSpacer(1); - sizer_right->Add(boxButtons, 0, wxTOP | wxEXPAND, 5); + sizer_right->Add(m_label_scan_disabled, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, space5); + sizer_right->Add(boxButtons, 0, wxEXPAND | wxTOP, space5); // main sizer wxBoxSizer* const sizer_main = new wxBoxSizer(wxHORIZONTAL); - sizer_main->Add(sizer_left, 1, wxEXPAND | wxALL, 5); - sizer_main->Add(sizer_right, 0, wxEXPAND | wxALL, 5); + sizer_main->AddSpacer(space5); + sizer_main->Add(sizer_cheat_search_results, 1, wxEXPAND | wxTOP | wxBOTTOM, space5); + sizer_main->AddSpacer(space5); + sizer_main->Add(sizer_right, 0, wxEXPAND | wxTOP | wxBOTTOM, space5); + sizer_main->AddSpacer(space5); SetSizerAndFit(sizer_main); } +void CheatSearchTab::UpdateGUI() +{ + bool core_running = Core::IsRunning(); + m_btn_init_scan->Enable(core_running); + m_btn_next_scan->Enable(core_running && m_scan_is_initialized); + m_label_scan_disabled->Show(!core_running); + + Layout(); // Label shown/hidden may require layout adjustment +} + void CheatSearchTab::OnNewScanClicked(wxCommandEvent& WXUNUSED(event)) { if (!Core::IsRunningAndStarted()) @@ -136,6 +167,7 @@ void CheatSearchTab::OnNewScanClicked(wxCommandEvent& WXUNUSED(event)) m_search_results.reserve(Memory::RAM_SIZE / m_search_type_size); // Enable the "Next Scan" button. + m_scan_is_initialized = true; m_btn_next_scan->Enable(); CheatSearchResult r; @@ -183,6 +215,21 @@ void CheatSearchTab::OnCreateARCodeClicked(wxCommandEvent&) arcode_dlg.ShowModal(); } +void CheatSearchTab::OnListViewItemActivated(wxListEvent&) +{ + if (!m_btn_create_code->IsEnabled()) + return; + + wxCommandEvent fake(wxEVT_BUTTON, m_btn_create_code->GetId()); + m_btn_create_code->GetEventHandler()->ProcessEvent(fake); +} + +void CheatSearchTab::OnListViewItemSelected(wxListEvent&) +{ + // Toggle "Create AR Code" Button + m_btn_create_code->Enable(m_lview_search_results->GetSelectedItemCount() > 0); +} + void CheatSearchTab::OnTimerUpdate(wxTimerEvent&) { if (Core::GetState() != Core::CORE_RUN) @@ -190,8 +237,8 @@ void CheatSearchTab::OnTimerUpdate(wxTimerEvent&) // Only update the currently visible list rows. long first = m_lview_search_results->GetTopItem(); - long last = - std::min(m_lview_search_results->GetItemCount(), m_lview_search_results->GetCountPerPage()); + long last = std::min<long>(m_lview_search_results->GetItemCount(), + first + m_lview_search_results->GetCountPerPage()); m_lview_search_results->Freeze(); @@ -207,8 +254,8 @@ void CheatSearchTab::OnTimerUpdate(wxTimerEvent&) void CheatSearchTab::UpdateCheatSearchResultsList() { m_update_timer.Stop(); - m_lview_search_results->ClearAll(); - ResetListViewColumns(); + m_lview_search_results->DeleteAllItems(); + m_btn_create_code->Disable(); wxString count_label = wxString::Format(_("Count: %lu"), (unsigned long)m_search_results.size()); if (m_search_results.size() > MAX_CHEAT_SEARCH_RESULTS_DISPLAY) @@ -312,14 +359,6 @@ void CheatSearchTab::FilterCheatSearchResults(u32 value, bool prev) m_search_results.swap(filtered_results); } -void CheatSearchTab::ResetListViewColumns() -{ - m_lview_search_results->AppendColumn(_("Address")); - m_lview_search_results->AppendColumn(_("Value")); - m_lview_search_results->AppendColumn(_("Value (float)")); - m_lview_search_results->AppendColumn(_("Value (double)")); -} - bool CheatSearchTab::ParseUserEnteredValue(u32* out) const { unsigned long parsed_x_val = 0; diff --git a/Source/Core/DolphinWX/Cheats/CheatSearchTab.h b/Source/Core/DolphinWX/Cheats/CheatSearchTab.h index 119eb88e47..6d53169d68 100644 --- a/Source/Core/DolphinWX/Cheats/CheatSearchTab.h +++ b/Source/Core/DolphinWX/Cheats/CheatSearchTab.h @@ -11,6 +11,7 @@ class wxButton; class wxChoice; class wxFocusEvent; +class wxListEvent; class wxListView; class wxRadioBox; class wxRadioButton; @@ -22,6 +23,8 @@ class CheatSearchTab final : public wxPanel public: CheatSearchTab(wxWindow* const parent); + void UpdateGUI(); + private: class CheatSearchResult final { @@ -34,25 +37,29 @@ private: void UpdateCheatSearchResultsList(); void UpdateCheatSearchResultItem(long index); void FilterCheatSearchResults(u32 value, bool prev); - void ResetListViewColumns(); bool ParseUserEnteredValue(u32* out) const; u32 SwapValue(u32 value) const; void OnNewScanClicked(wxCommandEvent&); void OnNextScanClicked(wxCommandEvent&); void OnCreateARCodeClicked(wxCommandEvent&); + void OnListViewItemActivated(wxListEvent&); + void OnListViewItemSelected(wxListEvent&); void OnTimerUpdate(wxTimerEvent&); std::vector<CheatSearchResult> m_search_results; unsigned int m_search_type_size; + bool m_scan_is_initialized = false; wxChoice* m_search_type; wxListView* m_lview_search_results; wxStaticText* m_label_results_count; wxTextCtrl* m_textctrl_value_x; + wxButton* m_btn_create_code; wxButton* m_btn_init_scan; wxButton* m_btn_next_scan; + wxStaticText* m_label_scan_disabled; wxRadioBox* m_data_sizes; diff --git a/Source/Core/DolphinWX/Cheats/CheatsWindow.cpp b/Source/Core/DolphinWX/Cheats/CheatsWindow.cpp index a6c6bad730..af4ec1c177 100644 --- a/Source/Core/DolphinWX/Cheats/CheatsWindow.cpp +++ b/Source/Core/DolphinWX/Cheats/CheatsWindow.cpp @@ -55,13 +55,15 @@ wxCheatsWindow::wxCheatsWindow(wxWindow* const parent) wxDIALOG_NO_PARENT) { // Create the GUI controls - Init_ChildControls(); + CreateGUI(); // load codes UpdateGUI(); wxTheApp->Bind(DOLPHIN_EVT_LOCAL_INI_CHANGED, &wxCheatsWindow::OnLocalGameIniModified, this); - SetSize(wxSize(-1, 600)); + SetIcons(WxUtils::GetDolphinIconBundle()); + SetLayoutAdaptationMode(wxDIALOG_ADAPTATION_MODE_ENABLED); + SetLayoutAdaptationLevel(wxDIALOG_ADAPTATION_STANDARD_SIZER); Center(); Show(); } @@ -71,8 +73,11 @@ wxCheatsWindow::~wxCheatsWindow() main_frame->g_CheatsWindow = nullptr; } -void wxCheatsWindow::Init_ChildControls() +void wxCheatsWindow::CreateGUI() { + const int space5 = FromDIP(5); + const int space10 = FromDIP(10); + // Main Notebook m_notebook_main = new wxNotebook(this, wxID_ANY); @@ -84,13 +89,15 @@ void wxCheatsWindow::Init_ChildControls() new ActionReplayCodesPanel(tab_cheats, ActionReplayCodesPanel::STYLE_SIDE_PANEL | ActionReplayCodesPanel::STYLE_MODIFY_BUTTONS); - wxBoxSizer* sizer_tab_cheats = new wxBoxSizer(wxHORIZONTAL); - sizer_tab_cheats->Add(m_ar_codes_panel, 1, wxEXPAND | wxALL, 5); + wxBoxSizer* sizer_tab_cheats = new wxBoxSizer(wxVERTICAL); + sizer_tab_cheats->AddSpacer(space5); + sizer_tab_cheats->Add(m_ar_codes_panel, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + sizer_tab_cheats->AddSpacer(space5); tab_cheats->SetSizerAndFit(sizer_tab_cheats); // Cheat Search Tab - wxPanel* const tab_cheat_search = new CheatSearchTab(m_notebook_main); + m_tab_cheat_search = new CheatSearchTab(m_notebook_main); // Log Tab m_tab_log = new wxPanel(m_notebook_main, wxID_ANY); @@ -105,25 +112,30 @@ void wxCheatsWindow::Init_ChildControls() &wxCheatsWindow::OnEvent_CheckBoxEnableLogging_StateChange, this); m_checkbox_log_ar->SetValue(ActionReplay::IsSelfLogging()); - m_textctrl_log = new wxTextCtrl(m_tab_log, wxID_ANY, "", wxDefaultPosition, wxSize(100, -1), - wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP); + m_textctrl_log = new wxTextCtrl(m_tab_log, wxID_ANY, wxEmptyString, wxDefaultPosition, + wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP); - wxBoxSizer* HStrip1 = new wxBoxSizer(wxHORIZONTAL); - HStrip1->Add(m_checkbox_log_ar, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - HStrip1->Add(button_updatelog, 0, wxALL, 5); - HStrip1->Add(button_clearlog, 0, wxALL, 5); + wxBoxSizer* log_control_sizer = new wxBoxSizer(wxHORIZONTAL); + log_control_sizer->Add(m_checkbox_log_ar, 0, wxALIGN_CENTER_VERTICAL); + log_control_sizer->Add(button_updatelog, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space10); + log_control_sizer->Add(button_clearlog, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space10); wxBoxSizer* sTabLog = new wxBoxSizer(wxVERTICAL); - sTabLog->Add(HStrip1, 0, wxALL, 5); - sTabLog->Add(m_textctrl_log, 1, wxALL | wxEXPAND, 5); + sTabLog->AddSpacer(space5); + sTabLog->Add(log_control_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space10); + sTabLog->AddSpacer(space5); + sTabLog->Add(m_textctrl_log, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + sTabLog->AddSpacer(space5); m_tab_log->SetSizerAndFit(sTabLog); + // Gecko tab + m_geckocode_panel = new Gecko::CodeConfigPanel(m_notebook_main); + // Add Tabs to Notebook m_notebook_main->AddPage(tab_cheats, _("AR Codes")); - m_geckocode_panel = new Gecko::CodeConfigPanel(m_notebook_main); m_notebook_main->AddPage(m_geckocode_panel, _("Gecko Codes")); - m_notebook_main->AddPage(tab_cheat_search, _("Cheat Search")); + m_notebook_main->AddPage(m_tab_cheat_search, _("Cheat Search")); m_notebook_main->AddPage(m_tab_log, _("Logging")); Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ApplyChanges_Press, this, wxID_APPLY); @@ -137,18 +149,23 @@ void wxCheatsWindow::Init_ChildControls() SetAffirmativeId(wxID_CANCEL); wxBoxSizer* const sMain = new wxBoxSizer(wxVERTICAL); - sMain->Add(m_notebook_main, 1, wxEXPAND | wxALL, 5); - sMain->Add(sButtons, 0, wxRIGHT | wxBOTTOM | wxALIGN_RIGHT, 5); + sMain->AddSpacer(space5); + sMain->Add(m_notebook_main, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + sMain->AddSpacer(space5); + sMain->Add(sButtons, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sMain->AddSpacer(space5); + sMain->SetMinSize(FromDIP(wxSize(-1, 600))); SetSizerAndFit(sMain); } -void wxCheatsWindow::OnEvent_ButtonClose_Press(wxCommandEvent& WXUNUSED(event)) +void wxCheatsWindow::OnEvent_ButtonClose_Press(wxCommandEvent&) { Close(); } -void wxCheatsWindow::OnEvent_Close(wxCloseEvent& ev) +void wxCheatsWindow::OnEvent_Close(wxCloseEvent&) { + // This dialog is created on the heap instead of the stack so we have to destroy ourself. Destroy(); } @@ -164,6 +181,7 @@ void wxCheatsWindow::UpdateGUI() m_gameini_local_path = File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini"; Load_ARCodes(); Load_GeckoCodes(); + m_tab_cheat_search->UpdateGUI(); // enable controls m_button_apply->Enable(Core::IsRunning()); @@ -194,8 +212,7 @@ void wxCheatsWindow::Load_ARCodes() void wxCheatsWindow::Load_GeckoCodes() { - m_geckocode_panel->LoadCodes(m_gameini_default, m_gameini_local, - SConfig::GetInstance().GetUniqueID(), true); + m_geckocode_panel->LoadCodes(m_gameini_default, m_gameini_local, m_game_id, true); } void wxCheatsWindow::OnNewARCodeCreated(wxCommandEvent& ev) @@ -243,7 +260,7 @@ void wxCheatsWindow::OnEvent_ApplyChanges_Press(wxCommandEvent& ev) ev.Skip(); } -void wxCheatsWindow::OnEvent_ButtonUpdateLog_Press(wxCommandEvent& WXUNUSED(event)) +void wxCheatsWindow::OnEvent_ButtonUpdateLog_Press(wxCommandEvent&) { wxBeginBusyCursor(); m_textctrl_log->Freeze(); @@ -284,7 +301,7 @@ void wxCheatsWindow::OnClearActionReplayLog(wxCommandEvent& event) OnEvent_ButtonUpdateLog_Press(event); } -void wxCheatsWindow::OnEvent_CheckBoxEnableLogging_StateChange(wxCommandEvent& WXUNUSED(event)) +void wxCheatsWindow::OnEvent_CheckBoxEnableLogging_StateChange(wxCommandEvent&) { ActionReplay::EnableSelfLogging(m_checkbox_log_ar->IsChecked()); } diff --git a/Source/Core/DolphinWX/Cheats/CheatsWindow.h b/Source/Core/DolphinWX/Cheats/CheatsWindow.h index 4302cb8af0..0d32bfb8b2 100644 --- a/Source/Core/DolphinWX/Cheats/CheatsWindow.h +++ b/Source/Core/DolphinWX/Cheats/CheatsWindow.h @@ -7,13 +7,13 @@ #include <cstddef> #include <string> #include <vector> -#include <wx/arrstr.h> #include <wx/dialog.h> #include <wx/panel.h> #include "Common/CommonTypes.h" #include "Common/IniFile.h" +class CheatSearchTab; class wxButton; class wxCheckBox; class wxNotebook; @@ -41,6 +41,7 @@ private: wxButton* m_button_apply; wxNotebook* m_notebook_main; + CheatSearchTab* m_tab_cheat_search; wxPanel* m_tab_log; wxCheckBox* m_checkbox_log_ar; @@ -57,7 +58,7 @@ private: bool m_ignore_ini_callback = false; - void Init_ChildControls(); + void CreateGUI(); void Load_ARCodes(); void Load_GeckoCodes(); diff --git a/Source/Core/DolphinWX/Cheats/CreateCodeDialog.cpp b/Source/Core/DolphinWX/Cheats/CreateCodeDialog.cpp index 32b4ac09a9..8f398bd513 100644 --- a/Source/Core/DolphinWX/Cheats/CreateCodeDialog.cpp +++ b/Source/Core/DolphinWX/Cheats/CreateCodeDialog.cpp @@ -19,8 +19,8 @@ CreateCodeDialog::CreateCodeDialog(wxWindow* const parent, const u32 address) : wxDialog(parent, wxID_ANY, _("Create AR Code")), m_code_address(address) { wxStaticText* const label_name = new wxStaticText(this, wxID_ANY, _("Name: ")); - m_textctrl_name = - new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(256, -1)); + m_textctrl_name = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, + wxDLG_UNIT(this, wxSize(180, -1))); wxStaticText* const label_code = new wxStaticText(this, wxID_ANY, _("Code: ")); m_textctrl_code = new wxTextCtrl(this, wxID_ANY, wxString::Format("0x%08x", address)); @@ -33,22 +33,31 @@ CreateCodeDialog::CreateCodeDialog(wxWindow* const parent, const u32 address) m_checkbox_use_hex->SetValue(true); wxBoxSizer* const sizer_value_label = new wxBoxSizer(wxHORIZONTAL); - sizer_value_label->Add(label_value, 0, wxRIGHT, 5); - sizer_value_label->Add(m_checkbox_use_hex); + const int space5 = FromDIP(5); + sizer_value_label->Add(label_value); + sizer_value_label->Add(m_checkbox_use_hex, 0, wxLEFT, space5); // main sizer wxBoxSizer* const sizer_main = new wxBoxSizer(wxVERTICAL); - sizer_main->Add(label_name, 0, wxALL, 5); - sizer_main->Add(m_textctrl_name, 0, wxALL, 5); - sizer_main->Add(label_code, 0, wxALL, 5); - sizer_main->Add(m_textctrl_code, 0, wxALL, 5); - sizer_main->Add(sizer_value_label, 0, wxALL, 5); - sizer_main->Add(m_textctrl_value, 0, wxALL, 5); - sizer_main->Add(CreateButtonSizer(wxOK | wxCANCEL | wxNO_DEFAULT), 0, wxALL, 5); + sizer_main->AddSpacer(space5); + sizer_main->Add(label_name, 0, wxLEFT | wxRIGHT, space5); + sizer_main->AddSpacer(space5); + sizer_main->Add(m_textctrl_name, 0, wxLEFT | wxRIGHT, space5); + sizer_main->AddSpacer(space5); + sizer_main->Add(label_code, 0, wxLEFT | wxRIGHT, space5); + sizer_main->AddSpacer(space5); + sizer_main->Add(m_textctrl_code, 0, wxLEFT | wxRIGHT, space5); + sizer_main->AddSpacer(space5); + sizer_main->Add(sizer_value_label, 0, wxLEFT | wxRIGHT, space5); + sizer_main->AddSpacer(space5); + sizer_main->Add(m_textctrl_value, 0, wxLEFT | wxRIGHT, space5); + sizer_main->AddSpacer(space5); + sizer_main->Add(CreateButtonSizer(wxOK | wxCANCEL | wxNO_DEFAULT), 0, wxEXPAND | wxLEFT | wxRIGHT, + space5); + sizer_main->AddSpacer(space5); + // NOTE: Use default wxCANCEL handling. Bind(wxEVT_BUTTON, &CreateCodeDialog::PressOK, this, wxID_OK); - Bind(wxEVT_BUTTON, &CreateCodeDialog::PressCancel, this, wxID_CANCEL); - Bind(wxEVT_CLOSE_WINDOW, &CreateCodeDialog::OnEvent_Close, this); SetSizerAndFit(sizer_main); SetFocus(); @@ -82,15 +91,6 @@ void CreateCodeDialog::PressOK(wxCommandEvent& ev) add_event.SetClientData(&new_cheat); GetParent()->GetEventHandler()->ProcessEvent(add_event); - Close(); -} - -void CreateCodeDialog::PressCancel(wxCommandEvent& ev) -{ - Close(); -} - -void CreateCodeDialog::OnEvent_Close(wxCloseEvent& ev) -{ - Destroy(); + // Allow base class to process. wxDialog will set the return code and hide the window. + ev.Skip(); } diff --git a/Source/Core/DolphinWX/Cheats/CreateCodeDialog.h b/Source/Core/DolphinWX/Cheats/CreateCodeDialog.h index 8eae79e564..6df209426c 100644 --- a/Source/Core/DolphinWX/Cheats/CreateCodeDialog.h +++ b/Source/Core/DolphinWX/Cheats/CreateCodeDialog.h @@ -26,6 +26,4 @@ private: wxCheckBox* m_checkbox_use_hex; void PressOK(wxCommandEvent&); - void PressCancel(wxCommandEvent&); - void OnEvent_Close(wxCloseEvent& ev); }; diff --git a/Source/Core/DolphinWX/Cheats/GeckoCodeDiag.cpp b/Source/Core/DolphinWX/Cheats/GeckoCodeDiag.cpp index 8625324b94..f2b275a9be 100644 --- a/Source/Core/DolphinWX/Cheats/GeckoCodeDiag.cpp +++ b/Source/Core/DolphinWX/Cheats/GeckoCodeDiag.cpp @@ -27,8 +27,9 @@ wxDEFINE_EVENT(DOLPHIN_EVT_GECKOCODE_TOGGLED, wxCommandEvent); namespace Gecko { -static const wxString wxstr_name(wxTRANSLATE("Name: ")), wxstr_notes(wxTRANSLATE("Notes: ")), - wxstr_creator(wxTRANSLATE("Creator: ")); +static const char str_name[] = wxTRANSLATE("Name: "); +static const char str_notes[] = wxTRANSLATE("Notes: "); +static const char str_creator[] = wxTRANSLATE("Creator: "); CodeConfigPanel::CodeConfigPanel(wxWindow* const parent) : wxPanel(parent) { @@ -36,40 +37,41 @@ CodeConfigPanel::CodeConfigPanel(wxWindow* const parent) : wxPanel(parent) m_listbox_gcodes->Bind(wxEVT_LISTBOX, &CodeConfigPanel::UpdateInfoBox, this); m_listbox_gcodes->Bind(wxEVT_CHECKLISTBOX, &CodeConfigPanel::ToggleCode, this); - m_infobox.label_name = new wxStaticText(this, wxID_ANY, wxGetTranslation(wxstr_name)); - m_infobox.label_creator = new wxStaticText(this, wxID_ANY, wxGetTranslation(wxstr_creator)); - m_infobox.label_notes = new wxStaticText(this, wxID_ANY, wxGetTranslation(wxstr_notes)); + m_infobox.label_name = new wxStaticText(this, wxID_ANY, wxGetTranslation(str_name)); + m_infobox.label_creator = new wxStaticText(this, wxID_ANY, wxGetTranslation(str_creator)); + m_infobox.label_notes = new wxStaticText(this, wxID_ANY, wxGetTranslation(str_notes)); m_infobox.textctrl_notes = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, - wxSize(64, -1), wxTE_MULTILINE | wxTE_READONLY); - m_infobox.listbox_codes = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 64)); + wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY); + m_infobox.listbox_codes = + new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDLG_UNIT(this, wxSize(-1, 48))); // TODO: buttons to add/edit codes // sizers + const int space5 = FromDIP(5); wxBoxSizer* const sizer_infobox = new wxBoxSizer(wxVERTICAL); - sizer_infobox->Add(m_infobox.label_name, 0, wxBOTTOM, 5); - sizer_infobox->Add(m_infobox.label_creator, 0, wxBOTTOM, 5); - sizer_infobox->Add(m_infobox.label_notes, 0, wxBOTTOM, 5); - sizer_infobox->Add(m_infobox.textctrl_notes, 0, wxBOTTOM | wxEXPAND, 5); - sizer_infobox->Add(m_infobox.listbox_codes, 1, wxEXPAND, 5); + sizer_infobox->Add(m_infobox.label_name); + sizer_infobox->Add(m_infobox.label_creator, 0, wxTOP, space5); + sizer_infobox->Add(m_infobox.label_notes, 0, wxTOP, space5); + sizer_infobox->Add(m_infobox.textctrl_notes, 0, wxEXPAND | wxTOP, space5); + sizer_infobox->Add(m_infobox.listbox_codes, 1, wxEXPAND | wxTOP, space5); // button sizer wxBoxSizer* const sizer_buttons = new wxBoxSizer(wxHORIZONTAL); - btn_download = new wxButton(this, wxID_ANY, _("Download Codes (WiiRD Database)"), - wxDefaultPosition, wxSize(128, -1)); + btn_download = new wxButton(this, wxID_ANY, _("Download Codes (WiiRD Database)")); btn_download->Disable(); btn_download->Bind(wxEVT_BUTTON, &CodeConfigPanel::DownloadCodes, this); sizer_buttons->AddStretchSpacer(1); - sizer_buttons->Add(btn_download, 1, wxEXPAND); - - // horizontal sizer - wxBoxSizer* const sizer_vert = new wxBoxSizer(wxVERTICAL); - sizer_vert->Add(sizer_infobox, 1, wxEXPAND); - sizer_vert->Add(sizer_buttons, 0, wxEXPAND | wxTOP, 5); + sizer_buttons->Add(WxUtils::GiveMinSizeDIP(btn_download, wxSize(128, -1)), 1, wxEXPAND); wxBoxSizer* const sizer_main = new wxBoxSizer(wxVERTICAL); - sizer_main->Add(m_listbox_gcodes, 1, wxALL | wxEXPAND, 5); - sizer_main->Add(sizer_vert, 0, wxALL | wxEXPAND, 5); + sizer_main->AddSpacer(space5); + sizer_main->Add(m_listbox_gcodes, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + sizer_main->AddSpacer(space5); + sizer_main->Add(sizer_infobox, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sizer_main->AddSpacer(space5); + sizer_main->Add(sizer_buttons, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sizer_main->AddSpacer(space5); SetSizerAndFit(sizer_main); } @@ -83,7 +85,7 @@ void CodeConfigPanel::UpdateCodeList(bool checkRunning) // add the codes to the listbox for (const GeckoCode& code : m_gcodes) { - m_listbox_gcodes->Append(StrToWxStr(code.name)); + m_listbox_gcodes->Append(m_listbox_gcodes->EscapeMnemonics(StrToWxStr(code.name))); if (code.enabled) { m_listbox_gcodes->Check(m_listbox_gcodes->GetCount() - 1, true); @@ -126,7 +128,7 @@ void CodeConfigPanel::UpdateInfoBox(wxCommandEvent&) if (sel > -1) { - m_infobox.label_name->SetLabel(wxGetTranslation(wxstr_name) + StrToWxStr(m_gcodes[sel].name)); + m_infobox.label_name->SetLabel(wxGetTranslation(str_name) + StrToWxStr(m_gcodes[sel].name)); // notes textctrl m_infobox.textctrl_notes->Clear(); @@ -136,7 +138,7 @@ void CodeConfigPanel::UpdateInfoBox(wxCommandEvent&) } m_infobox.textctrl_notes->ScrollLines(-99); // silly - m_infobox.label_creator->SetLabel(wxGetTranslation(wxstr_creator) + + m_infobox.label_creator->SetLabel(wxGetTranslation(str_creator) + StrToWxStr(m_gcodes[sel].creator)); // add codes to info listbox @@ -147,9 +149,9 @@ void CodeConfigPanel::UpdateInfoBox(wxCommandEvent&) } else { - m_infobox.label_name->SetLabel(wxGetTranslation(wxstr_name)); + m_infobox.label_name->SetLabel(wxGetTranslation(str_name)); m_infobox.textctrl_notes->Clear(); - m_infobox.label_creator->SetLabel(wxGetTranslation(wxstr_creator)); + m_infobox.label_creator->SetLabel(wxGetTranslation(str_creator)); } } diff --git a/Source/Core/DolphinWX/Config/AdvancedConfigPane.cpp b/Source/Core/DolphinWX/Config/AdvancedConfigPane.cpp index c245a358cb..406e76785f 100644 --- a/Source/Core/DolphinWX/Config/AdvancedConfigPane.cpp +++ b/Source/Core/DolphinWX/Config/AdvancedConfigPane.cpp @@ -7,9 +7,7 @@ #include <wx/checkbox.h> #include <wx/datectrl.h> #include <wx/dateevt.h> -#include <wx/gbsizer.h> #include <wx/sizer.h> -#include <wx/slider.h> #include <wx/stattext.h> #include <wx/time.h> #include <wx/timectrl.h> @@ -17,6 +15,7 @@ #include "Core/ConfigManager.h" #include "Core/Core.h" #include "DolphinWX/Config/AdvancedConfigPane.h" +#include "DolphinWX/DolphinSlider.h" AdvancedConfigPane::AdvancedConfigPane(wxWindow* parent, wxWindowID id) : wxPanel(parent, id) { @@ -29,7 +28,7 @@ void AdvancedConfigPane::InitializeGUI() { m_clock_override_checkbox = new wxCheckBox(this, wxID_ANY, _("Enable CPU Clock Override")); m_clock_override_slider = - new wxSlider(this, wxID_ANY, 100, 0, 150, wxDefaultPosition, wxSize(200, -1)); + new DolphinSlider(this, wxID_ANY, 100, 0, 150, wxDefaultPosition, FromDIP(wxSize(200, -1))); m_clock_override_text = new wxStaticText(this, wxID_ANY, ""); m_clock_override_checkbox->Bind(wxEVT_CHECKBOX, @@ -67,47 +66,47 @@ void AdvancedConfigPane::InitializeGUI() clock_override_description->Wrap(550); custom_rtc_description->Wrap(550); #else - clock_override_description->Wrap(400); - custom_rtc_description->Wrap(400); + clock_override_description->Wrap(FromDIP(400)); + custom_rtc_description->Wrap(FromDIP(400)); #endif - wxBoxSizer* const clock_override_checkbox_sizer = new wxBoxSizer(wxHORIZONTAL); - clock_override_checkbox_sizer->Add(m_clock_override_checkbox, 1, wxALL, 5); + const int space5 = FromDIP(5); wxBoxSizer* const clock_override_slider_sizer = new wxBoxSizer(wxHORIZONTAL); - clock_override_slider_sizer->Add(m_clock_override_slider, 1, wxALL, 5); - clock_override_slider_sizer->Add(m_clock_override_text, 1, wxALL, 5); - - wxBoxSizer* const clock_override_description_sizer = new wxBoxSizer(wxHORIZONTAL); - clock_override_description_sizer->Add(clock_override_description, 1, wxALL, 5); + clock_override_slider_sizer->Add(m_clock_override_slider, 1); + clock_override_slider_sizer->Add(m_clock_override_text, 1, wxLEFT, space5); wxStaticBoxSizer* const cpu_options_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("CPU Options")); - cpu_options_sizer->Add(clock_override_checkbox_sizer); - cpu_options_sizer->Add(clock_override_slider_sizer); - cpu_options_sizer->Add(clock_override_description_sizer); + cpu_options_sizer->AddSpacer(space5); + cpu_options_sizer->Add(m_clock_override_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + cpu_options_sizer->AddSpacer(space5); + cpu_options_sizer->Add(clock_override_slider_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + cpu_options_sizer->AddSpacer(space5); + cpu_options_sizer->Add(clock_override_description, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + cpu_options_sizer->AddSpacer(space5); - wxBoxSizer* const custom_rtc_checkbox_sizer = new wxBoxSizer(wxHORIZONTAL); - custom_rtc_checkbox_sizer->Add(m_custom_rtc_checkbox, 1, wxALL, 5); - - wxGridBagSizer* const custom_rtc_date_time_sizer = new wxGridBagSizer(); - custom_rtc_date_time_sizer->Add(m_custom_rtc_date_picker, wxGBPosition(0, 0), wxDefaultSpan, - wxEXPAND | wxALL, 5); - custom_rtc_date_time_sizer->Add(m_custom_rtc_time_picker, wxGBPosition(0, 1), wxDefaultSpan, - wxEXPAND | wxALL, 5); - - wxBoxSizer* const custom_rtc_description_sizer = new wxBoxSizer(wxHORIZONTAL); - custom_rtc_description_sizer->Add(custom_rtc_description, 1, wxALL, 5); + wxFlexGridSizer* const custom_rtc_date_time_sizer = + new wxFlexGridSizer(2, wxSize(space5, space5)); + custom_rtc_date_time_sizer->Add(m_custom_rtc_date_picker, 0, wxEXPAND); + custom_rtc_date_time_sizer->Add(m_custom_rtc_time_picker, 0, wxEXPAND); wxStaticBoxSizer* const custom_rtc_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Custom RTC Options")); - custom_rtc_sizer->Add(custom_rtc_checkbox_sizer); - custom_rtc_sizer->Add(custom_rtc_date_time_sizer); - custom_rtc_sizer->Add(custom_rtc_description_sizer); + custom_rtc_sizer->AddSpacer(space5); + custom_rtc_sizer->Add(m_custom_rtc_checkbox, 0, wxLEFT | wxRIGHT, space5); + custom_rtc_sizer->AddSpacer(space5); + custom_rtc_sizer->Add(custom_rtc_date_time_sizer, 0, wxLEFT | wxRIGHT, space5); + custom_rtc_sizer->AddSpacer(space5); + custom_rtc_sizer->Add(custom_rtc_description, 0, wxLEFT | wxRIGHT, space5); + custom_rtc_sizer->AddSpacer(space5); wxBoxSizer* const main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->Add(cpu_options_sizer, 0, wxEXPAND | wxALL, 5); - main_sizer->Add(custom_rtc_sizer, 0, wxEXPAND | wxALL, 5); + main_sizer->AddSpacer(space5); + main_sizer->Add(cpu_options_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); + main_sizer->Add(custom_rtc_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); SetSizer(main_sizer); } diff --git a/Source/Core/DolphinWX/Config/AdvancedConfigPane.h b/Source/Core/DolphinWX/Config/AdvancedConfigPane.h index 64296ef2f3..4b19e35c55 100644 --- a/Source/Core/DolphinWX/Config/AdvancedConfigPane.h +++ b/Source/Core/DolphinWX/Config/AdvancedConfigPane.h @@ -6,9 +6,9 @@ #include <wx/panel.h> +class DolphinSlider; class wxCheckBox; class wxDatePickerCtrl; -class wxSlider; class wxStaticText; class wxTimePickerCtrl; @@ -37,7 +37,7 @@ private: u32 m_temp_time; wxCheckBox* m_clock_override_checkbox; - wxSlider* m_clock_override_slider; + DolphinSlider* m_clock_override_slider; wxStaticText* m_clock_override_text; wxCheckBox* m_custom_rtc_checkbox; wxDatePickerCtrl* m_custom_rtc_date_picker; diff --git a/Source/Core/DolphinWX/Config/AudioConfigPane.cpp b/Source/Core/DolphinWX/Config/AudioConfigPane.cpp index 97dc4ca145..f1125700fc 100644 --- a/Source/Core/DolphinWX/Config/AudioConfigPane.cpp +++ b/Source/Core/DolphinWX/Config/AudioConfigPane.cpp @@ -9,7 +9,6 @@ #include <wx/gbsizer.h> #include <wx/radiobox.h> #include <wx/sizer.h> -#include <wx/slider.h> #include <wx/spinctrl.h> #include <wx/stattext.h> @@ -18,6 +17,7 @@ #include "Core/ConfigManager.h" #include "Core/Core.h" #include "DolphinWX/Config/AudioConfigPane.h" +#include "DolphinWX/DolphinSlider.h" #include "DolphinWX/WxUtils.h" AudioConfigPane::AudioConfigPane(wxWindow* parent, wxWindowID id) : wxPanel(parent, id) @@ -37,8 +37,8 @@ void AudioConfigPane::InitializeGUI() new wxRadioBox(this, wxID_ANY, _("DSP Emulator Engine"), wxDefaultPosition, wxDefaultSize, m_dsp_engine_strings, 0, wxRA_SPECIFY_ROWS); m_dpl2_decoder_checkbox = new wxCheckBox(this, wxID_ANY, _("Dolby Pro Logic II decoder")); - m_volume_slider = new wxSlider(this, wxID_ANY, 0, 0, 100, wxDefaultPosition, wxDefaultSize, - wxSL_VERTICAL | wxSL_INVERSE); + m_volume_slider = new DolphinSlider(this, wxID_ANY, 0, 0, 100, wxDefaultPosition, wxDefaultSize, + wxSL_VERTICAL | wxSL_INVERSE); m_volume_text = new wxStaticText(this, wxID_ANY, ""); m_audio_backend_choice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_audio_backend_strings); @@ -64,34 +64,51 @@ void AudioConfigPane::InitializeGUI() _("Enables Dolby Pro Logic II emulation using 5.1 surround. OpenAL or Pulse backends only.")); #endif + const int space5 = FromDIP(5); + wxStaticBoxSizer* const dsp_engine_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Sound Settings")); - dsp_engine_sizer->Add(m_dsp_engine_radiobox, 0, wxALL | wxEXPAND, 5); - dsp_engine_sizer->Add(m_dpl2_decoder_checkbox, 0, wxALL, 5); + dsp_engine_sizer->Add(m_dsp_engine_radiobox, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + dsp_engine_sizer->AddSpacer(space5); + dsp_engine_sizer->AddStretchSpacer(); + dsp_engine_sizer->Add(m_dpl2_decoder_checkbox, 0, wxLEFT | wxRIGHT, space5); + dsp_engine_sizer->AddStretchSpacer(); + dsp_engine_sizer->AddSpacer(space5); wxStaticBoxSizer* const volume_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Volume")); - volume_sizer->Add(m_volume_slider, 1, wxLEFT | wxRIGHT, 13); - volume_sizer->Add(m_volume_text, 0, wxALIGN_CENTER | wxALL, 5); + volume_sizer->Add(m_volume_slider, 1, wxALIGN_CENTER_HORIZONTAL); + volume_sizer->Add(m_volume_text, 0, wxALIGN_CENTER_HORIZONTAL | wxLEFT | wxRIGHT, space5); + volume_sizer->AddSpacer(space5); - wxGridBagSizer* const backend_grid_sizer = new wxGridBagSizer(); + wxGridBagSizer* const backend_grid_sizer = new wxGridBagSizer(space5, space5); backend_grid_sizer->Add(new wxStaticText(this, wxID_ANY, _("Audio Backend:")), wxGBPosition(0, 0), - wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5); - backend_grid_sizer->Add(m_audio_backend_choice, wxGBPosition(0, 1), wxDefaultSpan, wxALL, 5); + wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + backend_grid_sizer->Add(m_audio_backend_choice, wxGBPosition(0, 1), wxDefaultSpan, + wxALIGN_CENTER_VERTICAL); backend_grid_sizer->Add(new wxStaticText(this, wxID_ANY, _("Latency:")), wxGBPosition(1, 0), - wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5); - backend_grid_sizer->Add(m_audio_latency_spinctrl, wxGBPosition(1, 1), wxDefaultSpan, wxALL, 5); + wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + backend_grid_sizer->Add(m_audio_latency_spinctrl, wxGBPosition(1, 1), wxDefaultSpan, + wxALIGN_CENTER_VERTICAL); wxStaticBoxSizer* const backend_static_box_sizer = - new wxStaticBoxSizer(wxHORIZONTAL, this, _("Backend Settings")); - backend_static_box_sizer->Add(backend_grid_sizer, 0, wxEXPAND); + new wxStaticBoxSizer(wxVERTICAL, this, _("Backend Settings")); + backend_static_box_sizer->AddSpacer(space5); + backend_static_box_sizer->Add(backend_grid_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + backend_static_box_sizer->AddSpacer(space5); wxBoxSizer* const dsp_audio_sizer = new wxBoxSizer(wxHORIZONTAL); - dsp_audio_sizer->Add(dsp_engine_sizer, 1, wxEXPAND | wxALL, 5); - dsp_audio_sizer->Add(volume_sizer, 0, wxEXPAND | wxALL, 5); + dsp_audio_sizer->AddSpacer(space5); + dsp_audio_sizer->Add(dsp_engine_sizer, 1, wxEXPAND | wxTOP | wxBOTTOM, space5); + dsp_audio_sizer->AddSpacer(space5); + dsp_audio_sizer->Add(volume_sizer, 0, wxEXPAND | wxTOP | wxBOTTOM, space5); + dsp_audio_sizer->AddSpacer(space5); wxBoxSizer* const main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->Add(dsp_audio_sizer, 0, wxALL | wxEXPAND); - main_sizer->Add(backend_static_box_sizer, 0, wxALL | wxEXPAND, 5); + main_sizer->AddSpacer(space5); + main_sizer->Add(dsp_audio_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); + main_sizer->Add(backend_static_box_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); SetSizerAndFit(main_sizer); } @@ -180,10 +197,10 @@ void AudioConfigPane::PopulateBackendChoiceBox() for (const std::string& backend : AudioCommon::GetSoundBackends()) { m_audio_backend_choice->Append(wxGetTranslation(StrToWxStr(backend))); - - int num = m_audio_backend_choice->FindString(StrToWxStr(SConfig::GetInstance().sBackend)); - m_audio_backend_choice->SetSelection(num); } + + int num = m_audio_backend_choice->FindString(StrToWxStr(SConfig::GetInstance().sBackend)); + m_audio_backend_choice->SetSelection(num); } bool AudioConfigPane::SupportsVolumeChanges(const std::string& backend) diff --git a/Source/Core/DolphinWX/Config/AudioConfigPane.h b/Source/Core/DolphinWX/Config/AudioConfigPane.h index b2a988371c..877be31da5 100644 --- a/Source/Core/DolphinWX/Config/AudioConfigPane.h +++ b/Source/Core/DolphinWX/Config/AudioConfigPane.h @@ -8,10 +8,10 @@ #include <wx/arrstr.h> #include <wx/panel.h> +class DolphinSlider; class wxCheckBox; class wxChoice; class wxRadioBox; -class wxSlider; class wxSpinCtrl; class wxStaticText; @@ -39,7 +39,7 @@ private: wxRadioBox* m_dsp_engine_radiobox; wxCheckBox* m_dpl2_decoder_checkbox; - wxSlider* m_volume_slider; + DolphinSlider* m_volume_slider; wxStaticText* m_volume_text; wxChoice* m_audio_backend_choice; wxSpinCtrl* m_audio_latency_spinctrl; diff --git a/Source/Core/DolphinWX/Config/ConfigMain.cpp b/Source/Core/DolphinWX/Config/ConfigMain.cpp index b41e0c9ed7..1836167ede 100644 --- a/Source/Core/DolphinWX/Config/ConfigMain.cpp +++ b/Source/Core/DolphinWX/Config/ConfigMain.cpp @@ -76,16 +76,23 @@ void CConfigMain::CreateGUIControls() Notebook->AddPage(path_pane, _("Paths")); Notebook->AddPage(advanced_pane, _("Advanced")); + const int space5 = FromDIP(5); + wxBoxSizer* const main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->Add(Notebook, 1, wxEXPAND | wxALL, 5); - main_sizer->Add(CreateButtonSizer(wxCLOSE), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + main_sizer->AddSpacer(space5); + main_sizer->Add(Notebook, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); + main_sizer->Add(CreateButtonSizer(wxCLOSE), 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); #ifdef __APPLE__ main_sizer->SetMinSize(550, 0); #else - main_sizer->SetMinSize(400, 0); + main_sizer->SetMinSize(FromDIP(400), 0); #endif + SetLayoutAdaptationMode(wxDIALOG_ADAPTATION_MODE_ENABLED); + SetLayoutAdaptationLevel(wxDIALOG_ADAPTATION_STANDARD_SIZER); SetSizerAndFit(main_sizer); Center(); SetFocus(); diff --git a/Source/Core/DolphinWX/Config/GCAdapterConfigDiag.cpp b/Source/Core/DolphinWX/Config/GCAdapterConfigDiag.cpp index d15f4a5e4f..975502718a 100644 --- a/Source/Core/DolphinWX/Config/GCAdapterConfigDiag.cpp +++ b/Source/Core/DolphinWX/Config/GCAdapterConfigDiag.cpp @@ -15,10 +15,8 @@ wxDEFINE_EVENT(wxEVT_ADAPTER_UPDATE, wxCommandEvent); GCAdapterConfigDiag::GCAdapterConfigDiag(wxWindow* const parent, const wxString& name, const int tab_num) - : wxDialog(parent, wxID_ANY, name, wxPoint(128, -1)), m_pad_id(tab_num) + : wxDialog(parent, wxID_ANY, name), m_pad_id(tab_num) { - wxBoxSizer* const szr = new wxBoxSizer(wxVERTICAL); - wxCheckBox* const gamecube_rumble = new wxCheckBox(this, wxID_ANY, _("Rumble")); gamecube_rumble->SetValue(SConfig::GetInstance().m_AdapterRumble[m_pad_id]); gamecube_rumble->Bind(wxEVT_CHECKBOX, &GCAdapterConfigDiag::OnAdapterRumble, this); @@ -43,12 +41,16 @@ GCAdapterConfigDiag::GCAdapterConfigDiag(wxWindow* const parent, const wxString& } GCAdapter::SetAdapterCallback(std::bind(&GCAdapterConfigDiag::ScheduleAdapterUpdate, this)); + const int space5 = FromDIP(5); + + wxBoxSizer* const szr = new wxBoxSizer(wxVERTICAL); szr->Add(m_adapter_status, 0, wxEXPAND); szr->Add(gamecube_rumble, 0, wxEXPAND); szr->Add(gamecube_konga, 0, wxEXPAND); - szr->Add(CreateButtonSizer(wxCLOSE), 0, wxEXPAND | wxALL, 5); + szr->AddSpacer(space5); + szr->Add(CreateButtonSizer(wxCLOSE | wxNO_DEFAULT), 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + szr->AddSpacer(space5); - SetLayoutAdaptationMode(wxDIALOG_ADAPTATION_MODE_ENABLED); SetSizerAndFit(szr); Center(); diff --git a/Source/Core/DolphinWX/Config/GameCubeConfigPane.cpp b/Source/Core/DolphinWX/Config/GameCubeConfigPane.cpp index bd66b56705..06892a26bb 100644 --- a/Source/Core/DolphinWX/Config/GameCubeConfigPane.cpp +++ b/Source/Core/DolphinWX/Config/GameCubeConfigPane.cpp @@ -100,42 +100,51 @@ void GameCubeConfigPane::InitializeGUI() new wxButton(this, wxID_ANY, "...", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); m_memcard_path[1]->Bind(wxEVT_BUTTON, &GameCubeConfigPane::OnSlotBButtonClick, this); + const int space5 = FromDIP(5); + const int space10 = FromDIP(10); + // Populate the GameCube page - wxGridBagSizer* const sGamecubeIPLSettings = new wxGridBagSizer(); - sGamecubeIPLSettings->Add(m_skip_bios_checkbox, wxGBPosition(0, 0), wxGBSpan(1, 2), wxALL, 5); + wxGridBagSizer* const sGamecubeIPLSettings = new wxGridBagSizer(space5, space5); + sGamecubeIPLSettings->Add(m_skip_bios_checkbox, wxGBPosition(0, 0), wxGBSpan(1, 2)); sGamecubeIPLSettings->Add(new wxStaticText(this, wxID_ANY, _("System Language:")), - wxGBPosition(1, 0), wxDefaultSpan, - wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT | wxBOTTOM, 5); + wxGBPosition(1, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); sGamecubeIPLSettings->Add(m_system_lang_choice, wxGBPosition(1, 1), wxDefaultSpan, - wxLEFT | wxRIGHT | wxBOTTOM, 5); - sGamecubeIPLSettings->Add(m_override_lang_checkbox, wxGBPosition(2, 0), wxGBSpan(1, 2), wxALL, 5); + wxALIGN_CENTER_VERTICAL); + sGamecubeIPLSettings->Add(m_override_lang_checkbox, wxGBPosition(2, 0), wxGBSpan(1, 2)); wxStaticBoxSizer* const sbGamecubeIPLSettings = new wxStaticBoxSizer(wxVERTICAL, this, _("IPL Settings")); - sbGamecubeIPLSettings->Add(sGamecubeIPLSettings); + sbGamecubeIPLSettings->AddSpacer(space5); + sbGamecubeIPLSettings->Add(sGamecubeIPLSettings, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sbGamecubeIPLSettings->AddSpacer(space5); + wxStaticBoxSizer* const sbGamecubeDeviceSettings = new wxStaticBoxSizer(wxVERTICAL, this, _("Device Settings")); - wxGridBagSizer* const sbGamecubeEXIDevSettings = new wxGridBagSizer(10, 10); - + wxGridBagSizer* const gamecube_EXIDev_sizer = new wxGridBagSizer(space10, space10); for (int i = 0; i < 3; ++i) { - sbGamecubeEXIDevSettings->Add(GCEXIDeviceText[i], wxGBPosition(i, 0), wxDefaultSpan, - wxALIGN_CENTER_VERTICAL); - sbGamecubeEXIDevSettings->Add(m_exi_devices[i], wxGBPosition(i, 1), - wxGBSpan(1, (i < 2) ? 1 : 2), wxALIGN_CENTER_VERTICAL); + gamecube_EXIDev_sizer->Add(GCEXIDeviceText[i], wxGBPosition(i, 0), wxDefaultSpan, + wxALIGN_CENTER_VERTICAL); + gamecube_EXIDev_sizer->Add(m_exi_devices[i], wxGBPosition(i, 1), wxGBSpan(1, (i < 2) ? 1 : 2), + wxALIGN_CENTER_VERTICAL); if (i < 2) - sbGamecubeEXIDevSettings->Add(m_memcard_path[i], wxGBPosition(i, 2), wxDefaultSpan, - wxALIGN_CENTER_VERTICAL); + gamecube_EXIDev_sizer->Add(m_memcard_path[i], wxGBPosition(i, 2), wxDefaultSpan, + wxALIGN_CENTER_VERTICAL); if (NetPlay::IsNetPlayRunning()) m_exi_devices[i]->Disable(); } - sbGamecubeDeviceSettings->Add(sbGamecubeEXIDevSettings, 0, wxALL, 5); + sbGamecubeDeviceSettings->AddSpacer(space5); + sbGamecubeDeviceSettings->Add(gamecube_EXIDev_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sbGamecubeDeviceSettings->AddSpacer(space5); wxBoxSizer* const main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->Add(sbGamecubeIPLSettings, 0, wxEXPAND | wxALL, 5); - main_sizer->Add(sbGamecubeDeviceSettings, 0, wxEXPAND | wxALL, 5); + main_sizer->AddSpacer(space5); + main_sizer->Add(sbGamecubeIPLSettings, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); + main_sizer->Add(sbGamecubeDeviceSettings, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); SetSizer(main_sizer); } diff --git a/Source/Core/DolphinWX/Config/GeneralConfigPane.cpp b/Source/Core/DolphinWX/Config/GeneralConfigPane.cpp index 454fb3a59f..e5f81f7736 100644 --- a/Source/Core/DolphinWX/Config/GeneralConfigPane.cpp +++ b/Source/Core/DolphinWX/Config/GeneralConfigPane.cpp @@ -101,32 +101,51 @@ void GeneralConfigPane::InitializeGUI() m_throttler_choice->Bind(wxEVT_CHOICE, &GeneralConfigPane::OnThrottlerChoiceChanged, this); m_cpu_engine_radiobox->Bind(wxEVT_RADIOBOX, &GeneralConfigPane::OnCPUEngineRadioBoxChanged, this); + const int space5 = FromDIP(5); + wxBoxSizer* const throttler_sizer = new wxBoxSizer(wxHORIZONTAL); + throttler_sizer->AddSpacer(space5); throttler_sizer->Add(new wxStaticText(this, wxID_ANY, _("Speed Limit:")), 0, - wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT | wxBOTTOM, 5); - throttler_sizer->Add(m_throttler_choice, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 5); + wxALIGN_CENTER_VERTICAL | wxBOTTOM, space5); + throttler_sizer->AddSpacer(space5); + throttler_sizer->Add(m_throttler_choice, 0, wxALIGN_CENTER_VERTICAL | wxBOTTOM, space5); + throttler_sizer->AddSpacer(space5); wxStaticBoxSizer* const basic_settings_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Basic Settings")); - basic_settings_sizer->Add(m_dual_core_checkbox, 0, wxALL, 5); - basic_settings_sizer->Add(m_idle_skip_checkbox, 0, wxALL, 5); - basic_settings_sizer->Add(m_cheats_checkbox, 0, wxALL, 5); + basic_settings_sizer->AddSpacer(space5); + basic_settings_sizer->Add(m_dual_core_checkbox, 0, wxLEFT | wxRIGHT, space5); + basic_settings_sizer->AddSpacer(space5); + basic_settings_sizer->Add(m_idle_skip_checkbox, 0, wxLEFT | wxRIGHT, space5); + basic_settings_sizer->AddSpacer(space5); + basic_settings_sizer->Add(m_cheats_checkbox, 0, wxLEFT | wxRIGHT, space5); + basic_settings_sizer->AddSpacer(space5); basic_settings_sizer->Add(throttler_sizer); wxStaticBoxSizer* const analytics_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Usage Statistics Reporting Settings")); - analytics_sizer->Add(m_analytics_checkbox, 0, wxALL, 5); - analytics_sizer->Add(m_analytics_new_id, 0, wxALL, 5); + analytics_sizer->AddSpacer(space5); + analytics_sizer->Add(m_analytics_checkbox, 0, wxLEFT | wxRIGHT, space5); + analytics_sizer->AddSpacer(space5); + analytics_sizer->Add(m_analytics_new_id, 0, wxLEFT | wxRIGHT, space5); + analytics_sizer->AddSpacer(space5); wxStaticBoxSizer* const advanced_settings_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Advanced Settings")); - advanced_settings_sizer->Add(m_cpu_engine_radiobox, 0, wxALL, 5); - advanced_settings_sizer->Add(m_force_ntscj_checkbox, 0, wxALL, 5); + advanced_settings_sizer->AddSpacer(space5); + advanced_settings_sizer->Add(m_cpu_engine_radiobox, 0, wxLEFT | wxRIGHT, space5); + advanced_settings_sizer->AddSpacer(space5); + advanced_settings_sizer->Add(m_force_ntscj_checkbox, 0, wxLEFT | wxRIGHT, space5); + advanced_settings_sizer->AddSpacer(space5); wxBoxSizer* const main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->Add(basic_settings_sizer, 0, wxEXPAND | wxALL, 5); - main_sizer->Add(analytics_sizer, 0, wxEXPAND | wxALL, 5); - main_sizer->Add(advanced_settings_sizer, 0, wxEXPAND | wxALL, 5); + main_sizer->AddSpacer(space5); + main_sizer->Add(basic_settings_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); + main_sizer->Add(analytics_sizer, 0, wxEXPAND | wxLEFT | wxLEFT, space5); + main_sizer->AddSpacer(space5); + main_sizer->Add(advanced_settings_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); SetSizer(main_sizer); } diff --git a/Source/Core/DolphinWX/Config/InterfaceConfigPane.cpp b/Source/Core/DolphinWX/Config/InterfaceConfigPane.cpp index 988230612f..2927114e58 100644 --- a/Source/Core/DolphinWX/Config/InterfaceConfigPane.cpp +++ b/Source/Core/DolphinWX/Config/InterfaceConfigPane.cpp @@ -11,6 +11,7 @@ #include <wx/choice.h> #include <wx/gbsizer.h> #include <wx/language.h> +#include <wx/msgdlg.h> #include <wx/sizer.h> #include <wx/stattext.h> @@ -30,7 +31,7 @@ #include "DolphinWX/X11Utils.h" #endif -static const std::array<std::string, 29> language_ids = {{ +static const std::array<std::string, 29> language_ids{{ "", "ms", "ca", "cs", "da", "de", "en", "es", "fr", "hr", "it", "hu", "nl", @@ -115,27 +116,36 @@ void InterfaceConfigPane::InitializeGUI() m_interface_lang_choice->SetToolTip( _("Change the language of the user interface.\nRequires restart.")); - wxGridBagSizer* const language_and_theme_grid_sizer = new wxGridBagSizer(); + const int space5 = FromDIP(5); + + wxGridBagSizer* const language_and_theme_grid_sizer = new wxGridBagSizer(space5, space5); language_and_theme_grid_sizer->Add(new wxStaticText(this, wxID_ANY, _("Language:")), - wxGBPosition(0, 0), wxDefaultSpan, - wxALIGN_CENTER_VERTICAL | wxALL, 5); + wxGBPosition(0, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); language_and_theme_grid_sizer->Add(m_interface_lang_choice, wxGBPosition(0, 1), wxDefaultSpan, - wxALL, 5); + wxALIGN_CENTER_VERTICAL); language_and_theme_grid_sizer->Add(new wxStaticText(this, wxID_ANY, _("Theme:")), - wxGBPosition(1, 0), wxDefaultSpan, - wxALIGN_CENTER_VERTICAL | wxALL, 5); - language_and_theme_grid_sizer->Add(m_theme_choice, wxGBPosition(1, 1), wxDefaultSpan, wxALL, 5); + wxGBPosition(1, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + language_and_theme_grid_sizer->Add(m_theme_choice, wxGBPosition(1, 1), wxDefaultSpan, + wxALIGN_CENTER_VERTICAL); wxStaticBoxSizer* const main_static_box_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Interface Settings")); - main_static_box_sizer->Add(m_confirm_stop_checkbox, 0, wxALL, 5); - main_static_box_sizer->Add(m_panic_handlers_checkbox, 0, wxALL, 5); - main_static_box_sizer->Add(m_osd_messages_checkbox, 0, wxALL, 5); - main_static_box_sizer->Add(m_pause_focus_lost_checkbox, 0, wxALL, 5); - main_static_box_sizer->Add(language_and_theme_grid_sizer, 0, wxEXPAND | wxALL, 0); + main_static_box_sizer->AddSpacer(space5); + main_static_box_sizer->Add(m_confirm_stop_checkbox, 0, wxLEFT | wxRIGHT, space5); + main_static_box_sizer->AddSpacer(space5); + main_static_box_sizer->Add(m_panic_handlers_checkbox, 0, wxLEFT | wxRIGHT, space5); + main_static_box_sizer->AddSpacer(space5); + main_static_box_sizer->Add(m_osd_messages_checkbox, 0, wxLEFT | wxRIGHT, space5); + main_static_box_sizer->AddSpacer(space5); + main_static_box_sizer->Add(m_pause_focus_lost_checkbox, 0, wxLEFT | wxRIGHT, space5); + main_static_box_sizer->AddSpacer(space5); + main_static_box_sizer->Add(language_and_theme_grid_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_static_box_sizer->AddSpacer(space5); wxBoxSizer* const main_box_sizer = new wxBoxSizer(wxVERTICAL); - main_box_sizer->Add(main_static_box_sizer, 0, wxEXPAND | wxALL, 5); + main_box_sizer->AddSpacer(space5); + main_box_sizer->Add(main_static_box_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_box_sizer->AddSpacer(space5); SetSizer(main_box_sizer); } @@ -212,7 +222,10 @@ void InterfaceConfigPane::OnInterfaceLanguageChoiceChanged(wxCommandEvent& event { if (SConfig::GetInstance().m_InterfaceLanguage != language_ids[m_interface_lang_choice->GetSelection()]) - SuccessAlertT("You must restart Dolphin in order for the change to take effect."); + { + wxMessageBox(_("You must restart Dolphin in order for the change to take effect."), + _("Restart Required"), wxOK | wxICON_INFORMATION, this); + } SConfig::GetInstance().m_InterfaceLanguage = language_ids[m_interface_lang_choice->GetSelection()]; diff --git a/Source/Core/DolphinWX/Config/PathConfigPane.cpp b/Source/Core/DolphinWX/Config/PathConfigPane.cpp index 896073bb9a..08664dee29 100644 --- a/Source/Core/DolphinWX/Config/PathConfigPane.cpp +++ b/Source/Core/DolphinWX/Config/PathConfigPane.cpp @@ -77,45 +77,49 @@ void PathConfigPane::InitializeGUI() m_wii_sdcard_filepicker->Bind(wxEVT_FILEPICKER_CHANGED, &PathConfigPane::OnSdCardPathChanged, this); + const int space5 = FromDIP(5); + wxBoxSizer* const iso_button_sizer = new wxBoxSizer(wxHORIZONTAL); - iso_button_sizer->Add(m_recursive_iso_paths_checkbox, 0, wxALL | wxALIGN_CENTER); + iso_button_sizer->Add(m_recursive_iso_paths_checkbox, 0, wxALIGN_CENTER_VERTICAL); iso_button_sizer->AddStretchSpacer(); - iso_button_sizer->Add(m_add_iso_path_button, 0, wxALL); - iso_button_sizer->Add(m_remove_iso_path_button, 0, wxALL); + iso_button_sizer->Add(m_add_iso_path_button, 0, wxALIGN_CENTER_VERTICAL); + iso_button_sizer->Add(m_remove_iso_path_button, 0, wxALIGN_CENTER_VERTICAL); wxStaticBoxSizer* const iso_listbox_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("ISO Directories")); - iso_listbox_sizer->Add(m_iso_paths_listbox, 1, wxEXPAND | wxALL, 0); - iso_listbox_sizer->Add(iso_button_sizer, 0, wxEXPAND | wxALL, 5); + iso_listbox_sizer->Add(m_iso_paths_listbox, 1, wxEXPAND); + iso_listbox_sizer->AddSpacer(space5); + iso_listbox_sizer->Add(iso_button_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + iso_listbox_sizer->AddSpacer(space5); - wxGridBagSizer* const picker_sizer = new wxGridBagSizer(); + wxGridBagSizer* const picker_sizer = new wxGridBagSizer(space5, space5); picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("Default ISO:")), wxGBPosition(0, 0), - wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5); - picker_sizer->Add(m_default_iso_filepicker, wxGBPosition(0, 1), wxDefaultSpan, wxEXPAND | wxALL, - 5); + wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + picker_sizer->Add(m_default_iso_filepicker, wxGBPosition(0, 1), wxDefaultSpan, wxEXPAND); picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("DVD Root:")), wxGBPosition(1, 0), - wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5); - picker_sizer->Add(m_dvd_root_dirpicker, wxGBPosition(1, 1), wxDefaultSpan, wxEXPAND | wxALL, 5); + wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + picker_sizer->Add(m_dvd_root_dirpicker, wxGBPosition(1, 1), wxDefaultSpan, wxEXPAND); picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("Apploader:")), wxGBPosition(2, 0), - wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5); - picker_sizer->Add(m_apploader_path_filepicker, wxGBPosition(2, 1), wxDefaultSpan, - wxEXPAND | wxALL, 5); + wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + picker_sizer->Add(m_apploader_path_filepicker, wxGBPosition(2, 1), wxDefaultSpan, wxEXPAND); picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("Wii NAND Root:")), wxGBPosition(3, 0), - wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5); - picker_sizer->Add(m_nand_root_dirpicker, wxGBPosition(3, 1), wxDefaultSpan, wxEXPAND | wxALL, 5); + wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + picker_sizer->Add(m_nand_root_dirpicker, wxGBPosition(3, 1), wxDefaultSpan, wxEXPAND); picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("Dump Path:")), wxGBPosition(4, 0), - wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5); - picker_sizer->Add(m_dump_path_dirpicker, wxGBPosition(4, 1), wxDefaultSpan, wxEXPAND | wxALL, 5); + wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + picker_sizer->Add(m_dump_path_dirpicker, wxGBPosition(4, 1), wxDefaultSpan, wxEXPAND); picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("SD Card Path:")), wxGBPosition(5, 0), - wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5); - picker_sizer->Add(m_wii_sdcard_filepicker, wxGBPosition(5, 1), wxDefaultSpan, wxEXPAND | wxALL, - 5); + wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + picker_sizer->Add(m_wii_sdcard_filepicker, wxGBPosition(5, 1), wxDefaultSpan, wxEXPAND); picker_sizer->AddGrowableCol(1); // Populate the Paths page wxBoxSizer* const main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->Add(iso_listbox_sizer, 1, wxEXPAND | wxALL, 5); - main_sizer->Add(picker_sizer, 0, wxEXPAND | wxALL, 5); + main_sizer->AddSpacer(space5); + main_sizer->Add(iso_listbox_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); + main_sizer->Add(picker_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); SetSizer(main_sizer); } diff --git a/Source/Core/DolphinWX/Config/WiiConfigPane.cpp b/Source/Core/DolphinWX/Config/WiiConfigPane.cpp index 559bee6051..c53b5757d2 100644 --- a/Source/Core/DolphinWX/Config/WiiConfigPane.cpp +++ b/Source/Core/DolphinWX/Config/WiiConfigPane.cpp @@ -14,6 +14,7 @@ #include "Core/IPC_HLE/WII_IPC_HLE.h" #include "DiscIO/Enums.h" #include "DolphinWX/Config/WiiConfigPane.h" +#include "DolphinWX/DolphinSlider.h" #include "DolphinWX/WxUtils.h" WiiConfigPane::WiiConfigPane(wxWindow* parent, wxWindowID id) : wxPanel(parent, id) @@ -52,14 +53,10 @@ void WiiConfigPane::InitializeGUI() m_connect_keyboard_checkbox = new wxCheckBox(this, wxID_ANY, _("Connect USB Keyboard")); m_bt_sensor_bar_pos = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_bt_sensor_bar_pos_strings); - m_bt_sensor_bar_sens = new wxSlider(this, wxID_ANY, 0, 0, 4); - m_bt_speaker_volume = new wxSlider(this, wxID_ANY, 0, 0, 127); + m_bt_sensor_bar_sens = new DolphinSlider(this, wxID_ANY, 0, 0, 4); + m_bt_speaker_volume = new DolphinSlider(this, wxID_ANY, 0, 0, 127); m_bt_wiimote_motor = new wxCheckBox(this, wxID_ANY, _("Wiimote Motor")); - // With some GTK themes, no minimum size will be applied - so do this manually here - m_bt_sensor_bar_sens->SetMinSize(wxSize(100, -1)); - m_bt_speaker_volume->SetMinSize(wxSize(100, -1)); - m_screensaver_checkbox->Bind(wxEVT_CHECKBOX, &WiiConfigPane::OnScreenSaverCheckBoxChanged, this); m_pal60_mode_checkbox->Bind(wxEVT_CHECKBOX, &WiiConfigPane::OnPAL60CheckBoxChanged, this); m_aspect_ratio_choice->Bind(wxEVT_CHOICE, &WiiConfigPane::OnAspectRatioChoiceChanged, this); @@ -79,67 +76,78 @@ void WiiConfigPane::InitializeGUI() m_sd_card_checkbox->SetToolTip(_("Saved to /Wii/sd.raw (default size is 128mb)")); m_connect_keyboard_checkbox->SetToolTip(_("May cause slow down in Wii Menu and some games.")); - wxGridBagSizer* const misc_settings_grid_sizer = new wxGridBagSizer(); - misc_settings_grid_sizer->Add(m_screensaver_checkbox, wxGBPosition(0, 0), wxGBSpan(1, 2), wxALL, - 5); - misc_settings_grid_sizer->Add(m_pal60_mode_checkbox, wxGBPosition(1, 0), wxGBSpan(1, 2), wxALL, - 5); + const int space5 = FromDIP(5); + + wxGridBagSizer* const misc_settings_grid_sizer = new wxGridBagSizer(space5, space5); + misc_settings_grid_sizer->Add(m_screensaver_checkbox, wxGBPosition(0, 0), wxGBSpan(1, 2)); + misc_settings_grid_sizer->Add(m_pal60_mode_checkbox, wxGBPosition(1, 0), wxGBSpan(1, 2)); misc_settings_grid_sizer->Add(new wxStaticText(this, wxID_ANY, _("Aspect Ratio:")), - wxGBPosition(2, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, - 5); - misc_settings_grid_sizer->Add(m_aspect_ratio_choice, wxGBPosition(2, 1), wxDefaultSpan, wxALL, 5); + wxGBPosition(2, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + misc_settings_grid_sizer->Add(m_aspect_ratio_choice, wxGBPosition(2, 1), wxDefaultSpan, + wxALIGN_CENTER_VERTICAL); misc_settings_grid_sizer->Add(new wxStaticText(this, wxID_ANY, _("System Language:")), - wxGBPosition(3, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, - 5); - misc_settings_grid_sizer->Add(m_system_language_choice, wxGBPosition(3, 1), wxDefaultSpan, wxALL, - 5); + wxGBPosition(3, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + misc_settings_grid_sizer->Add(m_system_language_choice, wxGBPosition(3, 1), wxDefaultSpan, + wxALIGN_CENTER_VERTICAL); auto* const bt_sensor_bar_pos_sizer = new wxBoxSizer(wxHORIZONTAL); bt_sensor_bar_pos_sizer->Add(new wxStaticText(this, wxID_ANY, _("Min")), 0, wxALIGN_CENTER_VERTICAL); - bt_sensor_bar_pos_sizer->Add(m_bt_sensor_bar_sens); + bt_sensor_bar_pos_sizer->Add(m_bt_sensor_bar_sens, 0, wxALIGN_CENTER_VERTICAL); bt_sensor_bar_pos_sizer->Add(new wxStaticText(this, wxID_ANY, _("Max")), 0, wxALIGN_CENTER_VERTICAL); auto* const bt_speaker_volume_sizer = new wxBoxSizer(wxHORIZONTAL); bt_speaker_volume_sizer->Add(new wxStaticText(this, wxID_ANY, _("Min")), 0, wxALIGN_CENTER_VERTICAL); - bt_speaker_volume_sizer->Add(m_bt_speaker_volume); + bt_speaker_volume_sizer->Add(m_bt_speaker_volume, 0, wxALIGN_CENTER_VERTICAL); bt_speaker_volume_sizer->Add(new wxStaticText(this, wxID_ANY, _("Max")), 0, wxALIGN_CENTER_VERTICAL); - wxGridBagSizer* const bt_settings_grid_sizer = new wxGridBagSizer(); + wxGridBagSizer* const bt_settings_grid_sizer = new wxGridBagSizer(space5, space5); bt_settings_grid_sizer->Add(new wxStaticText(this, wxID_ANY, _("Sensor Bar Position:")), - wxGBPosition(0, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, - 5); - bt_settings_grid_sizer->Add(m_bt_sensor_bar_pos, wxGBPosition(0, 1), wxDefaultSpan, wxALL, 5); + wxGBPosition(0, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + bt_settings_grid_sizer->Add(m_bt_sensor_bar_pos, wxGBPosition(0, 1), wxDefaultSpan, + wxALIGN_CENTER_VERTICAL); bt_settings_grid_sizer->Add(new wxStaticText(this, wxID_ANY, _("IR Sensitivity:")), - wxGBPosition(1, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, - 5); - bt_settings_grid_sizer->Add(bt_sensor_bar_pos_sizer, wxGBPosition(1, 1), wxDefaultSpan, wxALL, 5); + wxGBPosition(1, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + bt_settings_grid_sizer->Add(bt_sensor_bar_pos_sizer, wxGBPosition(1, 1), wxDefaultSpan, + wxALIGN_CENTER_VERTICAL); bt_settings_grid_sizer->Add(new wxStaticText(this, wxID_ANY, _("Speaker Volume:")), - wxGBPosition(2, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, - 5); - bt_settings_grid_sizer->Add(bt_speaker_volume_sizer, wxGBPosition(2, 1), wxDefaultSpan, wxALL, 5); - bt_settings_grid_sizer->Add(m_bt_wiimote_motor, wxGBPosition(3, 0), wxGBSpan(1, 2), wxALL, 5); + wxGBPosition(2, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + bt_settings_grid_sizer->Add(bt_speaker_volume_sizer, wxGBPosition(2, 1), wxDefaultSpan, + wxALIGN_CENTER_VERTICAL); + bt_settings_grid_sizer->Add(m_bt_wiimote_motor, wxGBPosition(3, 0), wxGBSpan(1, 2), + wxALIGN_CENTER_VERTICAL); wxStaticBoxSizer* const misc_settings_static_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Misc Settings")); - misc_settings_static_sizer->Add(misc_settings_grid_sizer); + misc_settings_static_sizer->AddSpacer(space5); + misc_settings_static_sizer->Add(misc_settings_grid_sizer, 0, wxLEFT | wxRIGHT, space5); + misc_settings_static_sizer->AddSpacer(space5); wxStaticBoxSizer* const device_settings_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Device Settings")); - device_settings_sizer->Add(m_sd_card_checkbox, 0, wxALL, 5); - device_settings_sizer->Add(m_connect_keyboard_checkbox, 0, wxALL, 5); + device_settings_sizer->AddSpacer(space5); + device_settings_sizer->Add(m_sd_card_checkbox, 0, wxLEFT | wxRIGHT, space5); + device_settings_sizer->AddSpacer(space5); + device_settings_sizer->Add(m_connect_keyboard_checkbox, 0, wxLEFT | wxRIGHT, space5); + device_settings_sizer->AddSpacer(space5); auto* const bt_settings_static_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Wii Remote Settings")); - bt_settings_static_sizer->Add(bt_settings_grid_sizer); + bt_settings_static_sizer->AddSpacer(space5); + bt_settings_static_sizer->Add(bt_settings_grid_sizer, 0, wxLEFT | wxRIGHT, space5); + bt_settings_static_sizer->AddSpacer(space5); wxBoxSizer* const main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->Add(misc_settings_static_sizer, 0, wxEXPAND | wxALL, 5); - main_sizer->Add(device_settings_sizer, 0, wxEXPAND | wxALL, 5); - main_sizer->Add(bt_settings_static_sizer, 0, wxEXPAND | wxALL, 5); + main_sizer->AddSpacer(space5); + main_sizer->Add(misc_settings_static_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); + main_sizer->Add(device_settings_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); + main_sizer->Add(bt_settings_static_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); SetSizer(main_sizer); } diff --git a/Source/Core/DolphinWX/Config/WiiConfigPane.h b/Source/Core/DolphinWX/Config/WiiConfigPane.h index ac482277fd..a185751c24 100644 --- a/Source/Core/DolphinWX/Config/WiiConfigPane.h +++ b/Source/Core/DolphinWX/Config/WiiConfigPane.h @@ -13,6 +13,7 @@ namespace DiscIO enum class Language; } +class DolphinSlider; class wxCheckBox; class wxChoice; class wxSlider; @@ -53,7 +54,7 @@ private: wxChoice* m_aspect_ratio_choice; wxChoice* m_bt_sensor_bar_pos; - wxSlider* m_bt_sensor_bar_sens; - wxSlider* m_bt_speaker_volume; + DolphinSlider* m_bt_sensor_bar_sens; + DolphinSlider* m_bt_speaker_volume; wxCheckBox* m_bt_wiimote_motor; }; diff --git a/Source/Core/DolphinWX/ControllerConfigDiag.cpp b/Source/Core/DolphinWX/ControllerConfigDiag.cpp index 58ddcbaf8a..6f489fa270 100644 --- a/Source/Core/DolphinWX/ControllerConfigDiag.cpp +++ b/Source/Core/DolphinWX/ControllerConfigDiag.cpp @@ -13,7 +13,7 @@ #include <wx/msgdlg.h> #include <wx/radiobut.h> #include <wx/sizer.h> -#include <wx/slider.h> +#include <wx/statbox.h> #include <wx/stattext.h> #include "Common/CommonTypes.h" @@ -29,11 +29,12 @@ #include "Core/HotkeyManager.h" #include "Core/IPC_HLE/WII_IPC_HLE.h" #include "Core/IPC_HLE/WII_IPC_HLE_Device_usb_bt_real.h" -#include "Core/Movie.h" #include "Core/NetPlayProto.h" #include "DolphinWX/Config/GCAdapterConfigDiag.h" #include "DolphinWX/ControllerConfigDiag.h" +#include "DolphinWX/DolphinSlider.h" #include "DolphinWX/InputConfigDiag.h" +#include "DolphinWX/WxUtils.h" #include "InputCommon/GCAdapter.h" #if defined(HAVE_XRANDR) && HAVE_XRANDR @@ -47,15 +48,18 @@ ControllerConfigDiag::ControllerConfigDiag(wxWindow* const parent) _("Steering Wheel"), _("Dance Mat"), _("DK Bongos"), _("GBA"), _("Keyboard")}}; - wxBoxSizer* const main_sizer = new wxBoxSizer(wxVERTICAL); + const int space5 = FromDIP(5); // Combine all UI controls into their own encompassing sizer. - wxBoxSizer* control_sizer = new wxBoxSizer(wxVERTICAL); - control_sizer->Add(CreateGamecubeSizer(), 0, wxEXPAND | wxALL, 5); - control_sizer->Add(CreateWiimoteConfigSizer(), 0, wxEXPAND | wxALL, 5); - - main_sizer->Add(control_sizer, 0, wxEXPAND); - main_sizer->Add(CreateButtonSizer(wxCLOSE), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + wxBoxSizer* const main_sizer = new wxBoxSizer(wxVERTICAL); + main_sizer->AddSpacer(space5); + main_sizer->Add(CreateGamecubeSizer(), 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); + main_sizer->Add(CreateWiimoteConfigSizer(), 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); + main_sizer->Add(CreateButtonSizer(wxCLOSE | wxNO_DEFAULT), 0, wxEXPAND | wxLEFT | wxRIGHT, + space5); + main_sizer->AddSpacer(space5); Bind(wxEVT_CLOSE_WINDOW, &ControllerConfigDiag::OnClose, this); Bind(wxEVT_BUTTON, &ControllerConfigDiag::OnCloseButton, this, wxID_CLOSE); @@ -131,25 +135,31 @@ void ControllerConfigDiag::UpdateUI() } } -wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer() +wxSizer* ControllerConfigDiag::CreateGamecubeSizer() { - wxStaticBoxSizer* const gamecube_static_sizer = - new wxStaticBoxSizer(wxVERTICAL, this, _("GameCube Controllers")); - wxFlexGridSizer* const gamecube_flex_sizer = new wxFlexGridSizer(3, 5, 5); + const int space5 = FromDIP(5); + + auto* gamecube_static_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("GameCube Controllers")); + auto* gamecube_flex_sizer = new wxFlexGridSizer(3, space5, space5); gamecube_flex_sizer->AddGrowableCol(1); + gamecube_static_sizer->AddSpacer(space5); + gamecube_static_sizer->Add(gamecube_flex_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + gamecube_static_sizer->AddSpacer(space5); wxStaticText* pad_labels[4]; wxChoice* pad_type_choices[4]; for (int i = 0; i < 4; i++) { - pad_labels[i] = new wxStaticText(this, wxID_ANY, wxString::Format(_("Port %i"), i + 1)); + pad_labels[i] = new wxStaticText(gamecube_static_sizer->GetStaticBox(), wxID_ANY, + wxString::Format(_("Port %i"), i + 1)); // Create an ID for the config button. const wxWindowID button_id = wxWindow::NewControlId(); m_gc_port_from_config_id.emplace(button_id, i); m_gc_port_configure_button[i] = - new wxButton(this, button_id, _("Configure"), wxDefaultPosition, wxSize(100, -1)); + new wxButton(gamecube_static_sizer->GetStaticBox(), button_id, _("Configure"), + wxDefaultPosition, wxDLG_UNIT(this, wxSize(60, -1))); m_gc_port_configure_button[i]->Bind(wxEVT_BUTTON, &ControllerConfigDiag::OnGameCubeConfigButton, this); @@ -157,8 +167,9 @@ wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer() const wxWindowID choice_id = wxWindow::NewControlId(); m_gc_port_from_choice_id.emplace(choice_id, i); - pad_type_choices[i] = new wxChoice(this, choice_id, wxDefaultPosition, wxDefaultSize, - m_gc_pad_type_strs.size(), m_gc_pad_type_strs.data()); + pad_type_choices[i] = + new wxChoice(gamecube_static_sizer->GetStaticBox(), choice_id, wxDefaultPosition, + wxDefaultSize, m_gc_pad_type_strs.size(), m_gc_pad_type_strs.data()); pad_type_choices[i]->Bind(wxEVT_CHOICE, &ControllerConfigDiag::OnGameCubePortChanged, this); @@ -199,35 +210,38 @@ wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer() // Add to the sizer gamecube_flex_sizer->Add(pad_labels[i], 0, wxALIGN_CENTER_VERTICAL); - gamecube_flex_sizer->Add(pad_type_choices[i], 0, wxALIGN_CENTER_VERTICAL | wxEXPAND); - gamecube_flex_sizer->Add(m_gc_port_configure_button[i], 1, wxEXPAND); + gamecube_flex_sizer->Add(WxUtils::GiveMinSize(pad_type_choices[i], wxDefaultSize), 0, wxEXPAND); + gamecube_flex_sizer->Add(m_gc_port_configure_button[i], 0, wxEXPAND); } - gamecube_static_sizer->Add(gamecube_flex_sizer, 0, wxEXPAND | wxALL, 5); - gamecube_static_sizer->AddSpacer(5); - return gamecube_static_sizer; } -wxStaticBoxSizer* ControllerConfigDiag::CreateWiimoteConfigSizer() +wxSizer* ControllerConfigDiag::CreateWiimoteConfigSizer() { + const int space5 = FromDIP(5); + const int space20 = FromDIP(20); + auto* const box = new wxStaticBoxSizer(wxVERTICAL, this, _("Wiimotes")); m_passthrough_bt_radio = new wxRadioButton(this, wxID_ANY, _("Passthrough a Bluetooth adapter"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); m_passthrough_bt_radio->Bind(wxEVT_RADIOBUTTON, &ControllerConfigDiag::OnBluetoothModeChanged, this); - box->Add(m_passthrough_bt_radio, 0, wxLEFT | wxRIGHT | wxTOP | wxEXPAND, 5); - box->Add(CreatePassthroughBTConfigSizer(), 0, wxALL | wxEXPAND, 5); + box->AddSpacer(space5); + box->Add(m_passthrough_bt_radio, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + box->AddSpacer(space5); + box->Add(CreatePassthroughBTConfigSizer(), 0, wxEXPAND | wxLEFT | wxRIGHT, space5); - box->AddSpacer(10); + box->AddSpacer(space20); m_emulated_bt_radio = new wxRadioButton(this, wxID_ANY, _("Emulate the Wii's Bluetooth adapter")); m_emulated_bt_radio->Bind(wxEVT_RADIOBUTTON, &ControllerConfigDiag::OnBluetoothModeChanged, this); - box->Add(m_emulated_bt_radio, 0, wxALL | wxEXPAND, 5); - box->Add(CreateEmulatedBTConfigSizer(), 0, wxALL | wxEXPAND, 5); - box->AddSpacer(5); + box->Add(m_emulated_bt_radio, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + box->AddSpacer(space5); + box->Add(CreateEmulatedBTConfigSizer(), 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + box->AddSpacer(space5); if (SConfig::GetInstance().m_bt_passthrough_enabled) m_passthrough_bt_radio->SetValue(true); @@ -237,46 +251,45 @@ wxStaticBoxSizer* ControllerConfigDiag::CreateWiimoteConfigSizer() return box; } -wxBoxSizer* ControllerConfigDiag::CreatePassthroughBTConfigSizer() +wxSizer* ControllerConfigDiag::CreatePassthroughBTConfigSizer() { - auto* const sizer = new wxBoxSizer(wxVERTICAL); - m_passthrough_sync_text = new wxStaticText(this, wxID_ANY, _("Sync real Wiimotes and pair them")); m_passthrough_sync_btn = - new wxButton(this, wxID_ANY, _("Sync"), wxDefaultPosition, wxSize(100, -1)); + new wxButton(this, wxID_ANY, _("Sync"), wxDefaultPosition, wxDLG_UNIT(this, wxSize(60, -1))); m_passthrough_sync_btn->Bind(wxEVT_BUTTON, &ControllerConfigDiag::OnPassthroughScanButton, this); m_passthrough_reset_text = new wxStaticText(this, wxID_ANY, _("Reset all saved Wiimote pairings")); m_passthrough_reset_btn = - new wxButton(this, wxID_ANY, _("Reset"), wxDefaultPosition, wxSize(100, -1)); + new wxButton(this, wxID_ANY, _("Reset"), wxDefaultPosition, wxDLG_UNIT(this, wxSize(60, -1))); m_passthrough_reset_btn->Bind(wxEVT_BUTTON, &ControllerConfigDiag::OnPassthroughResetButton, this); - auto* const sync_sizer = new wxBoxSizer(wxHORIZONTAL); - sync_sizer->Add(m_passthrough_sync_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - sync_sizer->AddStretchSpacer(); - sync_sizer->Add(m_passthrough_sync_btn, 0, wxEXPAND); - auto* const reset_sizer = new wxBoxSizer(wxHORIZONTAL); - reset_sizer->Add(m_passthrough_reset_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - reset_sizer->AddStretchSpacer(); - reset_sizer->Add(m_passthrough_reset_btn, 0, wxEXPAND); + const int space5 = FromDIP(5); - sizer->Add(sync_sizer, 0, wxEXPAND); - sizer->AddSpacer(5); - sizer->Add(reset_sizer, 0, wxEXPAND); - return sizer; + auto* grid = new wxFlexGridSizer(3, space5, space5); + grid->AddGrowableCol(1); + + grid->Add(m_passthrough_sync_text, 0, wxALIGN_CENTER_VERTICAL); + grid->AddSpacer(1); + grid->Add(m_passthrough_sync_btn, 0, wxEXPAND); + + grid->Add(m_passthrough_reset_text, 0, wxALIGN_CENTER_VERTICAL); + grid->AddSpacer(1); + grid->Add(m_passthrough_reset_btn, 0, wxEXPAND); + + return grid; } -wxBoxSizer* ControllerConfigDiag::CreateEmulatedBTConfigSizer() +wxSizer* ControllerConfigDiag::CreateEmulatedBTConfigSizer() { - static const std::array<wxString, 4> src_choices = { + const std::array<wxString, 4> src_choices{ {_("None"), _("Emulated Wiimote"), _("Real Wiimote"), _("Hybrid Wiimote")}}; - auto* const sizer = new wxBoxSizer(wxVERTICAL); + const int space5 = FromDIP(5); // Source selector grid - auto* const grid = new wxFlexGridSizer(3, 5, 5); + auto* const grid = new wxFlexGridSizer(3, space5, space5); grid->AddGrowableCol(1); for (unsigned int i = 0; i < MAX_WIIMOTES; ++i) @@ -296,53 +309,58 @@ wxBoxSizer* ControllerConfigDiag::CreateEmulatedBTConfigSizer() src_choices.size(), src_choices.data()); m_wiimote_sources[i]->Bind(wxEVT_CHOICE, &ControllerConfigDiag::OnWiimoteSourceChanged, this); - m_wiimote_configure_button[i] = - new wxButton(this, config_bt_id, _("Configure"), wxDefaultPosition, wxSize(100, -1)); + m_wiimote_configure_button[i] = new wxButton( + this, config_bt_id, _("Configure"), wxDefaultPosition, wxDLG_UNIT(this, wxSize(60, -1))); m_wiimote_configure_button[i]->Bind(wxEVT_BUTTON, &ControllerConfigDiag::OnWiimoteConfigButton, this); grid->Add(m_wiimote_labels[i], 0, wxALIGN_CENTER_VERTICAL); - grid->Add(m_wiimote_sources[i], 0, wxALIGN_CENTER_VERTICAL | wxEXPAND); - grid->Add(m_wiimote_configure_button[i], 1, wxEXPAND); + grid->Add(WxUtils::GiveMinSize(m_wiimote_sources[i], wxDefaultSize), 0, wxEXPAND); + grid->Add(m_wiimote_configure_button[i], 0, wxEXPAND); } - sizer->Add(grid, 0, wxEXPAND); - sizer->AddSpacer(5); - // Scanning controls m_enable_continuous_scanning = new wxCheckBox(this, wxID_ANY, _("Continuous Scanning")); m_enable_continuous_scanning->Bind(wxEVT_CHECKBOX, &ControllerConfigDiag::OnContinuousScanning, this); m_enable_continuous_scanning->SetValue(SConfig::GetInstance().m_WiimoteContinuousScanning); - m_refresh_wm_button = - new wxButton(this, wxID_ANY, _("Refresh"), wxDefaultPosition, wxSize(100, -1)); + m_refresh_wm_button = new wxButton(this, wxID_ANY, _("Refresh"), wxDefaultPosition, + wxDLG_UNIT(this, wxSize(60, -1))); m_refresh_wm_button->Bind(wxEVT_BUTTON, &ControllerConfigDiag::OnWiimoteRefreshButton, this); m_unsupported_bt_text = new wxStaticText(this, wxID_ANY, _("A supported Bluetooth device could not be found,\n" "so you must connect Wiimotes manually.")); m_unsupported_bt_text->Show(!WiimoteReal::g_wiimote_scanner.IsReady()); - sizer->Add(m_unsupported_bt_text, 0, wxALIGN_CENTER | wxALL, 5); - - auto* const scanning_sizer = new wxBoxSizer(wxHORIZONTAL); - scanning_sizer->Add(m_enable_continuous_scanning, 0, wxALIGN_CENTER_VERTICAL); - scanning_sizer->AddStretchSpacer(); - scanning_sizer->Add(m_refresh_wm_button, 0, wxALL | wxEXPAND); - sizer->Add(scanning_sizer, 0, wxEXPAND); // Balance Board m_balance_board_checkbox = new wxCheckBox(this, wxID_ANY, _("Real Balance Board")); m_balance_board_checkbox->Bind(wxEVT_CHECKBOX, &ControllerConfigDiag::OnBalanceBoardChanged, this); m_balance_board_checkbox->SetValue(g_wiimote_sources[WIIMOTE_BALANCE_BOARD] == WIIMOTE_SRC_REAL); - sizer->Add(m_balance_board_checkbox); - sizer->AddSpacer(5); // Speaker data m_enable_speaker_data = new wxCheckBox(this, wxID_ANY, _("Enable Speaker Data")); m_enable_speaker_data->Bind(wxEVT_CHECKBOX, &ControllerConfigDiag::OnEnableSpeaker, this); m_enable_speaker_data->SetValue(SConfig::GetInstance().m_WiimoteEnableSpeaker); - sizer->Add(m_enable_speaker_data); + + auto* const checkbox_sizer = new wxBoxSizer(wxVERTICAL); + checkbox_sizer->Add(m_enable_continuous_scanning); + checkbox_sizer->AddSpacer(space5); + checkbox_sizer->Add(m_balance_board_checkbox); + checkbox_sizer->AddSpacer(space5); + checkbox_sizer->Add(m_enable_speaker_data); + + auto* const scanning_sizer = new wxBoxSizer(wxHORIZONTAL); + scanning_sizer->Add(checkbox_sizer, 1, wxEXPAND); + scanning_sizer->Add(m_refresh_wm_button, 0, wxALIGN_TOP); + + auto* const sizer = new wxBoxSizer(wxVERTICAL); + sizer->Add(grid, 0, wxEXPAND); + sizer->AddSpacer(space5); + sizer->Add(m_unsupported_bt_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, space5); + sizer->AddSpacer(space5); + sizer->Add(scanning_sizer, 0, wxEXPAND); return sizer; } @@ -428,21 +446,21 @@ void ControllerConfigDiag::OnGameCubeConfigButton(wxCommandEvent& event) if (SConfig::GetInstance().m_SIDevice[port_num] == SIDEVICE_GC_KEYBOARD) { - InputConfigDialog m_ConfigFrame(this, *key_plugin, _("GameCube Controller Configuration"), - port_num); - m_ConfigFrame.ShowModal(); + InputConfigDialog config_diag(this, *key_plugin, _("GameCube Keyboard Configuration"), + port_num); + config_diag.ShowModal(); } else if (SConfig::GetInstance().m_SIDevice[port_num] == SIDEVICE_WIIU_ADAPTER) { - GCAdapterConfigDiag m_ConfigFramg(this, _("Wii U Gamecube Controller Adapter Configuration"), - port_num); - m_ConfigFramg.ShowModal(); + GCAdapterConfigDiag config_diag(this, _("Wii U Gamecube Controller Adapter Configuration"), + port_num); + config_diag.ShowModal(); } else { - InputConfigDialog m_ConfigFrame(this, *pad_plugin, _("GameCube Controller Configuration"), - port_num); - m_ConfigFrame.ShowModal(); + InputConfigDialog config_diag(this, *pad_plugin, _("GameCube Controller Configuration"), + port_num); + config_diag.ShowModal(); } HotkeyManagerEmu::Enable(true); diff --git a/Source/Core/DolphinWX/ControllerConfigDiag.h b/Source/Core/DolphinWX/ControllerConfigDiag.h index 48a8e4b9fe..1f4bdab5cc 100644 --- a/Source/Core/DolphinWX/ControllerConfigDiag.h +++ b/Source/Core/DolphinWX/ControllerConfigDiag.h @@ -17,7 +17,6 @@ class InputConfig; class wxCheckBox; class wxChoice; class wxRadioButton; -class wxStaticBoxSizer; class wxStaticText; class ControllerConfigDiag final : public wxDialog @@ -28,10 +27,10 @@ public: private: void UpdateUI(); - wxStaticBoxSizer* CreateGamecubeSizer(); - wxStaticBoxSizer* CreateWiimoteConfigSizer(); - wxBoxSizer* CreatePassthroughBTConfigSizer(); - wxBoxSizer* CreateEmulatedBTConfigSizer(); + wxSizer* CreateGamecubeSizer(); + wxSizer* CreateWiimoteConfigSizer(); + wxSizer* CreatePassthroughBTConfigSizer(); + wxSizer* CreateEmulatedBTConfigSizer(); void OnClose(wxCloseEvent& event); void OnCloseButton(wxCommandEvent& event); diff --git a/Source/Core/DolphinWX/Debugger/BreakpointDlg.cpp b/Source/Core/DolphinWX/Debugger/BreakpointDlg.cpp index 9665bb271e..b9d1bca401 100644 --- a/Source/Core/DolphinWX/Debugger/BreakpointDlg.cpp +++ b/Source/Core/DolphinWX/Debugger/BreakpointDlg.cpp @@ -23,11 +23,15 @@ BreakPointDlg::BreakPointDlg(CBreakPointWindow* _Parent) m_pEditAddress = new wxTextCtrl(this, wxID_ANY, "80000000"); - wxBoxSizer* sMainSizer = new wxBoxSizer(wxVERTICAL); - sMainSizer->Add(m_pEditAddress, 0, wxEXPAND | wxALL, 5); - sMainSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL, 5); + const int space5 = FromDIP(5); + wxBoxSizer* main_szr = new wxBoxSizer(wxVERTICAL); + main_szr->AddSpacer(space5); + main_szr->Add(m_pEditAddress, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_szr->AddSpacer(space5); + main_szr->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_szr->AddSpacer(space5); - SetSizerAndFit(sMainSizer); + SetSizerAndFit(main_szr); SetFocus(); } diff --git a/Source/Core/DolphinWX/Debugger/BreakpointView.cpp b/Source/Core/DolphinWX/Debugger/BreakpointView.cpp index 743bfec2b8..d9242992ea 100644 --- a/Source/Core/DolphinWX/Debugger/BreakpointView.cpp +++ b/Source/Core/DolphinWX/Debugger/BreakpointView.cpp @@ -25,7 +25,7 @@ CBreakPointView::CBreakPointView(wxWindow* parent, const wxWindowID id) Refresh(); } -void CBreakPointView::Update() +void CBreakPointView::Repopulate() { ClearAll(); @@ -98,6 +98,6 @@ void CBreakPointView::DeleteCurrentSelection() u32 Address = (u32)GetItemData(item); PowerPC::breakpoints.Remove(Address); PowerPC::memchecks.Remove(Address); - Update(); + Repopulate(); } } diff --git a/Source/Core/DolphinWX/Debugger/BreakpointView.h b/Source/Core/DolphinWX/Debugger/BreakpointView.h index a7d37bb5bb..79a1283a45 100644 --- a/Source/Core/DolphinWX/Debugger/BreakpointView.h +++ b/Source/Core/DolphinWX/Debugger/BreakpointView.h @@ -11,6 +11,6 @@ class CBreakPointView : public wxListCtrl public: CBreakPointView(wxWindow* parent, const wxWindowID id); - void Update() override; + void Repopulate(); void DeleteCurrentSelection(); }; diff --git a/Source/Core/DolphinWX/Debugger/BreakpointWindow.cpp b/Source/Core/DolphinWX/Debugger/BreakpointWindow.cpp index 133e563fea..881add3f9d 100644 --- a/Source/Core/DolphinWX/Debugger/BreakpointWindow.cpp +++ b/Source/Core/DolphinWX/Debugger/BreakpointWindow.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include <array> + // clang-format off #include <wx/bitmap.h> #include <wx/aui/framemanager.h> @@ -32,11 +34,15 @@ public: : DolphinAuiToolBar(parent, id, wxDefaultPosition, wxDefaultSize, wxAUI_TB_DEFAULT_STYLE | wxAUI_TB_TEXT) { - SetToolBitmapSize(wxSize(24, 24)); + wxSize bitmap_size = FromDIP(wxSize(24, 24)); + SetToolBitmapSize(bitmap_size); - m_Bitmaps[Toolbar_Delete] = WxUtils::LoadResourceBitmap("toolbar_debugger_delete"); - m_Bitmaps[Toolbar_Add_BP] = WxUtils::LoadResourceBitmap("toolbar_add_breakpoint"); - m_Bitmaps[Toolbar_Add_MC] = WxUtils::LoadResourceBitmap("toolbar_add_memorycheck"); + static const std::array<const char* const, Num_Bitmaps> image_names{ + {"toolbar_debugger_delete", "toolbar_add_breakpoint", "toolbar_add_memorycheck"}}; + for (std::size_t i = 0; i < image_names.size(); ++i) + m_Bitmaps[i] = + WxUtils::LoadScaledResourceBitmap(image_names[i], this, bitmap_size, wxDefaultSize, + WxUtils::LSI_SCALE_DOWN | WxUtils::LSI_ALIGN_CENTER); AddTool(ID_DELETE, _("Delete"), m_Bitmaps[Toolbar_Delete]); Bind(wxEVT_TOOL, &CBreakPointWindow::OnDelete, parent, ID_DELETE); @@ -84,8 +90,6 @@ CBreakPointWindow::CBreakPointWindow(CCodeWindow* _pCodeWindow, wxWindow* parent const wxSize& size, long style) : wxPanel(parent, id, position, size, style, title), m_pCodeWindow(_pCodeWindow) { - Bind(wxEVT_CLOSE_WINDOW, &CBreakPointWindow::OnClose, this); - m_mgr.SetManagedWindow(this); m_mgr.SetFlags(wxAUI_MGR_DEFAULT | wxAUI_MGR_LIVE_RESIZE); @@ -108,15 +112,9 @@ CBreakPointWindow::~CBreakPointWindow() m_mgr.UnInit(); } -void CBreakPointWindow::OnClose(wxCloseEvent& event) -{ - SaveAll(); - event.Skip(); -} - void CBreakPointWindow::NotifyUpdate() { - m_BreakPointListView->Update(); + m_BreakPointListView->Repopulate(); } void CBreakPointWindow::OnDelete(wxCommandEvent& WXUNUSED(event)) diff --git a/Source/Core/DolphinWX/Debugger/BreakpointWindow.h b/Source/Core/DolphinWX/Debugger/BreakpointWindow.h index 67b8cb912f..3f07e81fb5 100644 --- a/Source/Core/DolphinWX/Debugger/BreakpointWindow.h +++ b/Source/Core/DolphinWX/Debugger/BreakpointWindow.h @@ -21,21 +21,21 @@ public: ~CBreakPointWindow(); void NotifyUpdate(); + void SaveAll(); + void LoadAll(); + +private: + friend class CBreakPointBar; void OnDelete(wxCommandEvent& WXUNUSED(event)); void OnClear(wxCommandEvent& WXUNUSED(event)); void OnAddBreakPoint(wxCommandEvent& WXUNUSED(event)); void OnAddMemoryCheck(wxCommandEvent& WXUNUSED(event)); void Event_SaveAll(wxCommandEvent& WXUNUSED(event)); - void SaveAll(); void Event_LoadAll(wxCommandEvent& WXUNUSED(event)); - void LoadAll(); + void OnSelectBP(wxListEvent& event); -private: wxAuiManager m_mgr; CBreakPointView* m_BreakPointListView; CCodeWindow* m_pCodeWindow; - - void OnClose(wxCloseEvent& event); - void OnSelectBP(wxListEvent& event); }; diff --git a/Source/Core/DolphinWX/Debugger/CodeView.cpp b/Source/Core/DolphinWX/Debugger/CodeView.cpp index 22119b6bac..8bc07036b3 100644 --- a/Source/Core/DolphinWX/Debugger/CodeView.cpp +++ b/Source/Core/DolphinWX/Debugger/CodeView.cpp @@ -53,9 +53,9 @@ CCodeView::CCodeView(DebugInterface* debuginterface, SymbolDB* symboldb, wxWindo wxWindowID Id) : wxControl(parent, Id), m_debugger(debuginterface), m_symbol_db(symboldb), m_plain(false), m_curAddress(debuginterface->GetPC()), m_align(debuginterface->GetInstructionSize(0)), - m_rowHeight(13), m_selection(0), m_oldSelection(0), m_selecting(false), m_lx(-1), m_ly(-1) + m_rowHeight(FromDIP(13)), m_left_col_width(FromDIP(LEFT_COL_WIDTH)), m_selection(0), + m_oldSelection(0), m_selecting(false) { - Bind(wxEVT_ERASE_BACKGROUND, &CCodeView::OnErase, this); Bind(wxEVT_PAINT, &CCodeView::OnPaint, this); Bind(wxEVT_MOUSEWHEEL, &CCodeView::OnScrollWheel, this); Bind(wxEVT_LEFT_DOWN, &CCodeView::OnMouseDown, this); @@ -65,6 +65,13 @@ CCodeView::CCodeView(DebugInterface* debuginterface, SymbolDB* symboldb, wxWindo Bind(wxEVT_RIGHT_UP, &CCodeView::OnMouseUpR, this); Bind(wxEVT_MENU, &CCodeView::OnPopupMenu, this); Bind(wxEVT_SIZE, &CCodeView::OnResize, this); + + // Disable the erase event, the entire window is being painted so the erase + // event will just cause unnecessary flicker. + SetBackgroundStyle(wxBG_STYLE_PAINT); +#if defined(__WXMSW__) || defined(__WXGTK__) + SetDoubleBuffered(true); +#endif } int CCodeView::YToAddress(int y) @@ -80,7 +87,7 @@ void CCodeView::OnMouseDown(wxMouseEvent& event) int x = event.m_x; int y = event.m_y; - if (x > 16) + if (x > m_left_col_width) { m_oldSelection = m_selection; m_selection = YToAddress(y); @@ -131,7 +138,7 @@ void CCodeView::OnMouseMove(wxMouseEvent& event) { wxRect rc = GetClientRect(); - if (event.m_leftDown && event.m_x > 16) + if (event.m_leftDown && event.m_x > m_left_col_width) { if (event.m_y < 0) { @@ -162,7 +169,7 @@ void CCodeView::RaiseEvent() void CCodeView::OnMouseUpL(wxMouseEvent& event) { - if (event.m_x > 16) + if (event.m_x > m_left_col_width) { m_curAddress = YToAddress(event.m_y); m_selecting = false; @@ -222,10 +229,6 @@ void CCodeView::InsertBlrNop(int Blr) void CCodeView::OnPopupMenu(wxCommandEvent& event) { -#if wxUSE_CLIPBOARD - wxTheClipboard->Open(); -#endif - switch (event.GetId()) { case IDM_GOTOINMEMVIEW: @@ -234,11 +237,15 @@ void CCodeView::OnPopupMenu(wxCommandEvent& event) #if wxUSE_CLIPBOARD case IDM_COPYADDRESS: + { + wxClipboardLocker locker; wxTheClipboard->SetData(new wxTextDataObject(wxString::Format("%08x", m_selection))); - break; + } + break; case IDM_COPYCODE: { + wxClipboardLocker locker; std::string disasm = m_debugger->Disassemble(m_selection); wxTheClipboard->SetData(new wxTextDataObject(StrToWxStr(disasm))); } @@ -246,8 +253,9 @@ void CCodeView::OnPopupMenu(wxCommandEvent& event) case IDM_COPYHEX: { - std::string temp = StringFromFormat("%08x", m_debugger->ReadInstruction(m_selection)); - wxTheClipboard->SetData(new wxTextDataObject(StrToWxStr(temp))); + wxClipboardLocker locker; + wxTheClipboard->SetData( + new wxTextDataObject(wxString::Format("%08x", m_debugger->ReadInstruction(m_selection)))); } break; @@ -266,6 +274,7 @@ void CCodeView::OnPopupMenu(wxCommandEvent& event) std::string disasm = m_debugger->Disassemble(addr); text += StringFromFormat("%08x: ", addr) + disasm + "\r\n"; } + wxClipboardLocker locker; wxTheClipboard->SetData(new wxTextDataObject(StrToWxStr(text))); } } @@ -283,6 +292,7 @@ void CCodeView::OnPopupMenu(wxCommandEvent& event) InsertBlrNop(0); Refresh(); break; + case IDM_INSERTNOP: InsertBlrNop(1); Refresh(); @@ -332,12 +342,11 @@ void CCodeView::OnPopupMenu(wxCommandEvent& event) case IDM_PATCHALERT: break; - } -#if wxUSE_CLIPBOARD - wxTheClipboard->Close(); -#endif - event.Skip(); + default: + event.Skip(); + break; + } } void CCodeView::OnMouseUpR(wxMouseEvent& event) @@ -363,104 +372,112 @@ void CCodeView::OnMouseUpR(wxMouseEvent& event) menu.Append(IDM_JITRESULTS, _("PPC vs X86"))->Enable(Core::IsRunning()); menu.Append(IDM_INSERTBLR, _("Insert &blr"))->Enable(Core::IsRunning()); menu.Append(IDM_INSERTNOP, _("Insert &nop"))->Enable(Core::IsRunning()); - menu.Append(IDM_PATCHALERT, _("Patch alert"))->Enable(Core::IsRunning()); + // menu.Append(IDM_PATCHALERT, _("Patch alert"))->Enable(Core::IsRunning()); PopupMenu(&menu); event.Skip(); } -void CCodeView::OnErase(wxEraseEvent& event) -{ -} - void CCodeView::OnPaint(wxPaintEvent& event) { // ------------------------- // General settings // ------------------------- - std::unique_ptr<wxGraphicsContext> ctx(wxGraphicsContext::Create(wxPaintDC(this))); + wxPaintDC paint_dc(this); wxRect rc = GetClientRect(); + int char_width; + paint_dc.SetFont(DebuggerFont); + { + wxFontMetrics metrics = paint_dc.GetFontMetrics(); + char_width = metrics.averageWidth; + if (metrics.height > m_rowHeight) + m_rowHeight = metrics.height; + } + + std::unique_ptr<wxGraphicsContext> ctx(wxGraphicsContext::Create(paint_dc)); + ctx->DisableOffset(); // Incompatible with matrix transforms ctx->SetFont(DebuggerFont, *wxBLACK); - wxDouble w, h; - ctx->GetTextExtent("0WJyq", &w, &h); - - if (h > m_rowHeight) - m_rowHeight = h; - - ctx->GetTextExtent("W", &w, &h); - int charWidth = w; - - struct branch + struct Branch { int src, dst, srcAddr; }; - branch branches[256]; - int numBranches = 0; - // TODO: Add any drawing code here... - int width = rc.width; - int numRows = ((rc.height / m_rowHeight) / 2) + 2; + Branch branches[256]; + int num_branches = 0; + const int num_rows = ((rc.height / m_rowHeight) / 2) + 2; + + const double scale = FromDIP(1024) / 1024.0; + const int pen_width = static_cast<int>(std::ceil(scale)); + const int col_width = rc.width - m_left_col_width; + const int text_col = m_left_col_width + pen_width / 2 + 1; // 1 unscaled pixel + const int bp_offset_x = FromDIP(LEFT_COL_WIDTH / 8); + const wxSize bp_size = FromDIP(wxSize(LEFT_COL_WIDTH * 3 / 4, LEFT_COL_WIDTH * 3 / 4)); + const int bp_offset_y = (m_rowHeight - bp_size.GetHeight()) / 2; // ------------ // ------------------------- // Colors and brushes // ------------------------- - const wxColour bgColor = *wxWHITE; - wxPen nullPen(bgColor); - wxPen currentPen(*wxBLACK_PEN); - wxPen selPen(*wxGREY_PEN); - nullPen.SetStyle(wxPENSTYLE_TRANSPARENT); - currentPen.SetStyle(wxPENSTYLE_SOLID); - wxBrush currentBrush(*wxLIGHT_GREY_BRUSH); - wxBrush pcBrush(*wxGREEN_BRUSH); - wxBrush bpBrush(*wxRED_BRUSH); + wxColour branch_color = wxTheColourDatabase->Find("PURPLE"); + wxColour blr_color = wxTheColourDatabase->Find("DARK GREEN"); + wxColour instr_color = wxTheColourDatabase->Find("VIOLET"); + wxGraphicsPen null_pen = ctx->CreatePen(*wxTRANSPARENT_PEN); + wxGraphicsPen focus_pen = ctx->CreatePen(wxPen(*wxBLACK, pen_width)); + wxGraphicsPen selection_pen = ctx->CreatePen(wxPen("GREY", pen_width)); + wxGraphicsBrush pc_brush = ctx->CreateBrush(*wxGREEN_BRUSH); + wxGraphicsBrush bp_brush = ctx->CreateBrush(*wxRED_BRUSH); + wxGraphicsBrush back_brush = ctx->CreateBrush(*wxWHITE_BRUSH); + wxGraphicsBrush null_brush = ctx->CreateBrush(*wxTRANSPARENT_BRUSH); - wxBrush bgBrush(bgColor); - wxBrush nullBrush(bgColor); - nullBrush.SetStyle(wxBRUSHSTYLE_TRANSPARENT); - - ctx->SetPen(nullPen); - ctx->SetBrush(bgBrush); - ctx->DrawRectangle(0, 0, 16, rc.height); - ctx->DrawRectangle(0, 0, rc.width, 5); // ------------ // ----------------------------- // Walk through all visible rows // ----------------------------- - for (int i = -numRows; i <= numRows; i++) + for (int i = -num_rows; i <= num_rows; i++) { unsigned int address = m_curAddress + (i * m_align); - int rowY1 = (rc.height / 2) + (m_rowHeight * i) - (m_rowHeight / 2); - int rowY2 = (rc.height / 2) + (m_rowHeight * i) + (m_rowHeight / 2); + int row_y = (rc.height / 2) + (m_rowHeight * i) - (m_rowHeight / 2); wxString temp = wxString::Format("%08x", address); u32 color = m_debugger->GetColor(address); - wxBrush rowBrush(wxColour(color >> 16, color >> 8, color)); - ctx->SetBrush(nullBrush); - ctx->SetPen(nullPen); - ctx->DrawRectangle(0, rowY1, 16, rowY2 - rowY1 + 2); - - if (m_selecting && (address == m_selection)) - ctx->SetPen(selPen); - else - ctx->SetPen(i == 0 ? currentPen : nullPen); + wxBrush row_brush(wxColour(color >> 16, color >> 8, color)); + ctx->SetBrush(back_brush); + ctx->SetPen(null_pen); + ctx->DrawRectangle(0, row_y, m_left_col_width, m_rowHeight); if (address == m_debugger->GetPC()) - ctx->SetBrush(pcBrush); + ctx->SetBrush(pc_brush); else - ctx->SetBrush(rowBrush); + ctx->SetBrush(row_brush); + + ctx->SetPen(null_pen); + ctx->DrawRectangle(m_left_col_width, row_y, col_width, m_rowHeight); + if (i == 0 || (m_selecting && address == m_selection)) + { + if (m_selecting && address == m_selection) + ctx->SetPen(selection_pen); + else + ctx->SetPen(focus_pen); + ctx->SetBrush(null_brush); + // In a graphics context, the border of a rectangle is drawn along the edge, + // it does not count towards the width of the rectangle (i.e. drawn right on + // the pixel boundary of the fill area, half inside, half outside. For example + // a rect with a 1px pen at (5,5)->(10,10) will have an actual screen size of + // (4.5,4.5)->(10.5,10.5) with the line being aliased on the half-pixels) + double offset = pen_width / 2.0; + ctx->DrawRectangle(m_left_col_width + offset, row_y + offset, col_width - pen_width, + m_rowHeight - pen_width); + } - ctx->DrawRectangle(16, rowY1, width, rowY2 - rowY1 + 1); - ctx->SetBrush(currentBrush); if (!m_plain) { // the address text is dark red - ctx->SetFont(DebuggerFont, wxColour("#600000")); - ctx->DrawText(temp, 17, rowY1); + ctx->SetFont(DebuggerFont, wxColour(0x60, 0x00, 0x00)); + ctx->DrawText(temp, text_col, row_y); ctx->SetFont(DebuggerFont, *wxBLACK); } @@ -488,31 +505,32 @@ void CCodeView::OnPaint(wxPaintEvent& event) { u32 offs = std::stoul(hex_str, nullptr, 16); - branches[numBranches].src = rowY1 + (m_rowHeight / 2); - branches[numBranches].srcAddr = (address / m_align); - branches[numBranches++].dst = - (int)(rowY1 + ((s64)(u32)offs - (s64)(u32)address) * m_rowHeight / m_align + + branches[num_branches].src = row_y + (m_rowHeight / 2); + branches[num_branches].srcAddr = (address / m_align); + branches[num_branches++].dst = + (int)(row_y + ((s64)(u32)offs - (s64)(u32)address) * m_rowHeight / m_align + m_rowHeight / 2); desc = StringFromFormat("-->%s", m_debugger->GetDescription(offs).c_str()); // the -> arrow illustrations are purple - ctx->SetFont(DebuggerFont, wxTheColourDatabase->Find("PURPLE")); + ctx->SetFont(DebuggerFont, branch_color); } else { ctx->SetFont(DebuggerFont, *wxBLACK); } - ctx->DrawText(StrToWxStr(operands), 17 + 17 * charWidth, rowY1); + ctx->DrawText(StrToWxStr(operands), text_col + 17 * char_width, row_y); // ------------ // Show blr as its' own color if (opcode == "blr") - ctx->SetFont(DebuggerFont, wxTheColourDatabase->Find("DARK GREEN")); + ctx->SetFont(DebuggerFont, blr_color); else - ctx->SetFont(DebuggerFont, wxTheColourDatabase->Find("VIOLET")); + ctx->SetFont(DebuggerFont, instr_color); - ctx->DrawText(StrToWxStr(opcode), 17 + (m_plain ? 1 * charWidth : 9 * charWidth), rowY1); + ctx->DrawText(StrToWxStr(opcode), text_col + (m_plain ? 1 * char_width : 9 * char_width), + row_y); if (desc.empty()) { @@ -527,15 +545,16 @@ void CCodeView::OnPaint(wxPaintEvent& event) // UnDecorateSymbolName(desc,temp,255,UNDNAME_COMPLETE); if (!desc.empty()) { - ctx->DrawText(StrToWxStr(desc), 17 + 35 * charWidth, rowY1); + ctx->DrawText(StrToWxStr(desc), text_col + 45 * char_width, row_y); } } // Show red breakpoint dot if (m_debugger->IsBreakpoint(address)) { - ctx->SetBrush(bpBrush); - ctx->DrawRectangle(2, rowY1 + 1, 11, 11); + ctx->SetPen(null_pen); + ctx->SetBrush(bp_brush); + ctx->DrawEllipse(bp_offset_x, row_y + bp_offset_y, bp_size.GetWidth(), bp_size.GetHeight()); } } } // end of for @@ -544,22 +563,24 @@ void CCodeView::OnPaint(wxPaintEvent& event) // ------------------------- // Colors and brushes // ------------------------- - ctx->SetPen(currentPen); + ctx->SetPen(focus_pen); - for (int i = 0; i < numBranches; i++) + wxGraphicsPath branch_path = ctx->CreatePath(); + + for (int i = 0; i < num_branches; ++i) { - int x = 17 + 49 * charWidth + (branches[i].srcAddr % 9) * 8; - MoveTo(x - 2, branches[i].src); + int x = text_col + 52 * char_width + (branches[i].srcAddr % 9) * 8; + branch_path.MoveToPoint(x - 2 * scale, branches[i].src); if (branches[i].dst < rc.height + 400 && branches[i].dst > -400) { - LineTo(ctx, x + 2, branches[i].src); - LineTo(ctx, x + 2, branches[i].dst); - LineTo(ctx, x - 4, branches[i].dst); + branch_path.AddLineToPoint(x + 2 * scale, branches[i].src); + branch_path.AddLineToPoint(x + 2 * scale, branches[i].dst); + branch_path.AddLineToPoint(x - 4 * scale, branches[i].dst); - MoveTo(x, branches[i].dst - 4); - LineTo(ctx, x - 4, branches[i].dst); - LineTo(ctx, x + 1, branches[i].dst + 5); + branch_path.MoveToPoint(x, branches[i].dst - 4 * scale); + branch_path.AddLineToPoint(x - 4 * scale, branches[i].dst); + branch_path.AddLineToPoint(x + 1 * scale, branches[i].dst + 5 * scale); } // else //{ @@ -575,18 +596,19 @@ void CCodeView::OnPaint(wxPaintEvent& event) // LineTo(ctx, x, branches[i].dst+4); // LineTo(ctx, x-2, branches[i].dst); } + + // If the pen width is odd then we need to offset the path so that lines are drawn in + // the middle of pixels instead of the edge so we don't get aliasing. + if (pen_width & 1) + { + wxGraphicsMatrix matrix = ctx->CreateMatrix(); + matrix.Translate(0.5, 0.5); + branch_path.Transform(matrix); + } + ctx->StrokePath(branch_path); // ------------ } -void CCodeView::LineTo(std::unique_ptr<wxGraphicsContext>& ctx, int x, int y) -{ - std::vector<wxPoint2DDouble> points{wxPoint2DDouble(m_lx, m_ly), wxPoint2DDouble(x, y)}; - - ctx->DrawLines(points.size(), points.data()); - m_lx = x; - m_ly = y; -} - void CCodeView::OnResize(wxSizeEvent& event) { Refresh(); diff --git a/Source/Core/DolphinWX/Debugger/CodeView.h b/Source/Core/DolphinWX/Debugger/CodeView.h index a29053dd1f..52e07e0cf8 100644 --- a/Source/Core/DolphinWX/Debugger/CodeView.h +++ b/Source/Core/DolphinWX/Debugger/CodeView.h @@ -40,7 +40,6 @@ public: void SetPlain() { m_plain = true; } private: void OnPaint(wxPaintEvent& event); - void OnErase(wxEraseEvent& event); void OnScrollWheel(wxMouseEvent& event); void OnMouseDown(wxMouseEvent& event); void OnMouseMove(wxMouseEvent& event); @@ -55,14 +54,6 @@ private: u32 AddrToBranch(u32 addr); void OnResize(wxSizeEvent& event); - void MoveTo(int x, int y) - { - m_lx = x; - m_ly = y; - } - - void LineTo(std::unique_ptr<wxGraphicsContext>& dc, int x, int y); - struct BlrStruct // for IDM_INSERTBLR { u32 address; @@ -70,6 +61,8 @@ private: }; std::vector<BlrStruct> m_blrList; + static constexpr int LEFT_COL_WIDTH = 16; + DebugInterface* m_debugger; SymbolDB* m_symbol_db; @@ -78,10 +71,9 @@ private: int m_curAddress; int m_align; int m_rowHeight; + int m_left_col_width; u32 m_selection; u32 m_oldSelection; bool m_selecting; - - int m_lx, m_ly; }; diff --git a/Source/Core/DolphinWX/Debugger/CodeWindow.cpp b/Source/Core/DolphinWX/Debugger/CodeWindow.cpp index aff5d48577..b3b4bf8ac6 100644 --- a/Source/Core/DolphinWX/Debugger/CodeWindow.cpp +++ b/Source/Core/DolphinWX/Debugger/CodeWindow.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include <array> #include <cstdio> #include <string> #include <vector> @@ -43,6 +44,7 @@ #include "DolphinWX/Debugger/CodeWindow.h" #include "DolphinWX/Debugger/DebuggerUIUtil.h" #include "DolphinWX/Debugger/JitWindow.h" +#include "DolphinWX/Debugger/MemoryWindow.h" #include "DolphinWX/Debugger/RegisterWindow.h" #include "DolphinWX/Debugger/WatchWindow.h" #include "DolphinWX/AuiToolBar.h" @@ -53,9 +55,8 @@ CCodeWindow::CCodeWindow(const SConfig& _LocalCoreStartupParameter, CFrame* parent, wxWindowID id, const wxPoint& position, const wxSize& size, long style, const wxString& name) - : wxPanel(parent, id, position, size, style, name), Parent(parent), m_RegisterWindow(nullptr), - m_WatchWindow(nullptr), m_BreakpointWindow(nullptr), m_MemoryWindow(nullptr), - m_JitWindow(nullptr), m_SoundWindow(nullptr), m_VideoWindow(nullptr), codeview(nullptr) + : wxPanel(parent, id, position, size, style, name), m_sibling_panels(), Parent(parent), + codeview(nullptr) { InitBitmaps(); @@ -87,21 +88,31 @@ CCodeWindow::CCodeWindow(const SConfig& _LocalCoreStartupParameter, CFrame* pare m_aui_manager.SetManagedWindow(this); m_aui_manager.SetFlags(wxAUI_MGR_DEFAULT | wxAUI_MGR_LIVE_RESIZE); - m_aui_manager.AddPane(m_aui_toolbar, - wxAuiPaneInfo().MinSize(150, -1).ToolbarPane().Top().Floatable(false)); - m_aui_manager.AddPane( - callstack, - wxAuiPaneInfo().MinSize(150, 100).Left().CloseButton(false).Floatable(false).Caption( - _("Callstack"))); - m_aui_manager.AddPane( - symbols, wxAuiPaneInfo().MinSize(150, 100).Left().CloseButton(false).Floatable(false).Caption( - _("Symbols"))); - m_aui_manager.AddPane( - calls, wxAuiPaneInfo().MinSize(150, 100).Left().CloseButton(false).Floatable(false).Caption( - _("Function calls"))); - m_aui_manager.AddPane( - callers, wxAuiPaneInfo().MinSize(150, 100).Left().CloseButton(false).Floatable(false).Caption( - _("Function callers"))); + m_aui_manager.AddPane(m_aui_toolbar, wxAuiPaneInfo().ToolbarPane().Top().Floatable(false)); + m_aui_manager.AddPane(callstack, wxAuiPaneInfo() + .MinSize(FromDIP(wxSize(150, 100))) + .Left() + .CloseButton(false) + .Floatable(false) + .Caption(_("Callstack"))); + m_aui_manager.AddPane(symbols, wxAuiPaneInfo() + .MinSize(FromDIP(wxSize(150, 100))) + .Left() + .CloseButton(false) + .Floatable(false) + .Caption(_("Symbols"))); + m_aui_manager.AddPane(calls, wxAuiPaneInfo() + .MinSize(FromDIP(wxSize(150, 100))) + .Left() + .CloseButton(false) + .Floatable(false) + .Caption(_("Function calls"))); + m_aui_manager.AddPane(callers, wxAuiPaneInfo() + .MinSize(FromDIP(wxSize(150, 100))) + .Left() + .CloseButton(false) + .Floatable(false) + .Caption(_("Function callers"))); m_aui_manager.AddPane(codeview, wxAuiPaneInfo().CenterPane().CloseButton(false).Floatable(false)); m_aui_manager.Update(); @@ -143,30 +154,30 @@ void CCodeWindow::OnHostMessage(wxCommandEvent& event) { case IDM_NOTIFY_MAP_LOADED: NotifyMapLoaded(); - if (m_BreakpointWindow) - m_BreakpointWindow->NotifyUpdate(); + if (HasPanel<CBreakPointWindow>()) + GetPanel<CBreakPointWindow>()->NotifyUpdate(); break; case IDM_UPDATE_DISASM_DIALOG: - Update(); - if (CPU::IsStepping()) - Parent->UpdateGUI(); - if (m_RegisterWindow) - m_RegisterWindow->NotifyUpdate(); - if (m_WatchWindow) - m_WatchWindow->NotifyUpdate(); + Repopulate(); + if (HasPanel<CRegisterWindow>()) + GetPanel<CRegisterWindow>()->NotifyUpdate(); + if (HasPanel<CWatchWindow>()) + GetPanel<CWatchWindow>()->NotifyUpdate(); + if (HasPanel<CMemoryWindow>()) + GetPanel<CMemoryWindow>()->Refresh(); break; case IDM_UPDATE_BREAKPOINTS: - if (m_BreakpointWindow) - m_BreakpointWindow->NotifyUpdate(); + Repopulate(); + if (HasPanel<CBreakPointWindow>()) + GetPanel<CBreakPointWindow>()->NotifyUpdate(); + if (HasPanel<CMemoryWindow>()) + GetPanel<CMemoryWindow>()->Refresh(); break; case IDM_UPDATE_JIT_PANE: - // Check if the JIT pane is in the AUI notebook. If not, add it and switch to it. - if (!m_JitWindow) - ToggleJitWindow(true); - m_JitWindow->ViewAddr(codeview->GetSelection()); + RequirePanel<CJitWindow>()->ViewAddr(codeview->GetSelection()); break; } } @@ -194,12 +205,12 @@ void CCodeWindow::OnCodeStep(wxCommandEvent& event) case IDM_SKIP: PC += 4; - Update(); + Repopulate(); break; case IDM_SETPC: PC = codeview->GetSelection(); - Update(); + Repopulate(); break; case IDM_GOTOPC: @@ -372,8 +383,12 @@ void CCodeWindow::StepOut() PowerPC::SetMode(old_mode); CPU::PauseAndLock(false, false); - Host_UpdateDisasmDialog(); - UpdateButtonStates(); + JumpToAddress(PC); + { + wxCommandEvent ev(wxEVT_HOST_COMMAND, IDM_UPDATE_DISASM_DIALOG); + GetEventHandler()->ProcessEvent(ev); + } + // Update all toolbars in the aui manager Parent->UpdateGUI(); } @@ -385,7 +400,7 @@ void CCodeWindow::ToggleBreakpoint() { if (codeview) codeview->ToggleBreakpoint(codeview->GetSelection()); - Update(); + Repopulate(); } } @@ -666,19 +681,18 @@ bool CCodeWindow::JITNoBlockLinking() // Toolbar void CCodeWindow::InitBitmaps() { - m_Bitmaps[Toolbar_Step] = WxUtils::LoadResourceBitmap("toolbar_debugger_step"); - m_Bitmaps[Toolbar_StepOver] = WxUtils::LoadResourceBitmap("toolbar_debugger_step_over"); - m_Bitmaps[Toolbar_StepOut] = WxUtils::LoadResourceBitmap("toolbar_debugger_step_out"); - m_Bitmaps[Toolbar_Skip] = WxUtils::LoadResourceBitmap("toolbar_debugger_skip"); - m_Bitmaps[Toolbar_GotoPC] = WxUtils::LoadResourceBitmap("toolbar_debugger_goto_pc"); - m_Bitmaps[Toolbar_SetPC] = WxUtils::LoadResourceBitmap("toolbar_debugger_set_pc"); + static constexpr std::array<const char* const, Toolbar_Debug_Bitmap_Max> s_image_names{ + {"toolbar_debugger_step", "toolbar_debugger_step_over", "toolbar_debugger_step_out", + "toolbar_debugger_skip", "toolbar_debugger_goto_pc", "toolbar_debugger_set_pc"}}; + const wxSize tool_size = Parent->GetToolbarBitmapSize(); + for (std::size_t i = 0; i < s_image_names.size(); ++i) + m_Bitmaps[i] = + WxUtils::LoadScaledResourceBitmap(s_image_names[i], Parent, tool_size, wxDefaultSize, + WxUtils::LSI_SCALE_DOWN | WxUtils::LSI_ALIGN_CENTER); } void CCodeWindow::PopulateToolbar(wxToolBar* toolBar) { - int w = m_Bitmaps[0].GetWidth(), h = m_Bitmaps[0].GetHeight(); - - toolBar->SetToolBitmapSize(wxSize(w, h)); WxUtils::AddToolbarButton(toolBar, IDM_STEP, _("Step"), m_Bitmaps[Toolbar_Step], _("Step into the next instruction")); WxUtils::AddToolbarButton(toolBar, IDM_STEPOVER, _("Step Over"), m_Bitmaps[Toolbar_StepOver], @@ -695,7 +709,7 @@ void CCodeWindow::PopulateToolbar(wxToolBar* toolBar) } // Update GUI -void CCodeWindow::Update() +void CCodeWindow::Repopulate() { if (!codeview) return; @@ -714,41 +728,28 @@ void CCodeWindow::UpdateButtonStates() bool Initialized = (Core::GetState() != Core::CORE_UNINITIALIZED); bool Pause = (Core::GetState() == Core::CORE_PAUSE); bool Stepping = CPU::IsStepping(); + bool can_step = Initialized && Stepping; wxToolBar* ToolBar = GetToolBar(); // Toolbar if (!ToolBar) return; - if (!Initialized) - { - ToolBar->EnableTool(IDM_STEPOVER, false); - ToolBar->EnableTool(IDM_STEPOUT, false); - ToolBar->EnableTool(IDM_SKIP, false); - } - else - { - if (!Stepping) - { - ToolBar->EnableTool(IDM_STEPOVER, false); - ToolBar->EnableTool(IDM_STEPOUT, false); - ToolBar->EnableTool(IDM_SKIP, false); - } - else - { - ToolBar->EnableTool(IDM_STEPOVER, true); - ToolBar->EnableTool(IDM_STEPOUT, true); - ToolBar->EnableTool(IDM_SKIP, true); - } - } - - ToolBar->EnableTool(IDM_STEP, Initialized && Stepping); + ToolBar->EnableTool(IDM_STEP, can_step); + ToolBar->EnableTool(IDM_STEPOVER, can_step); + ToolBar->EnableTool(IDM_STEPOUT, can_step); + ToolBar->EnableTool(IDM_SKIP, can_step); + ToolBar->EnableTool(IDM_SETPC, Pause); ToolBar->Realize(); // Menu bar // ------------------ GetMenuBar()->Enable(IDM_INTERPRETER, Pause); // CPU Mode + GetMenuBar()->Enable(IDM_STEP, can_step); + GetMenuBar()->Enable(IDM_STEPOVER, can_step); + GetMenuBar()->Enable(IDM_STEPOUT, can_step); + GetMenuBar()->Enable(IDM_JIT_NO_BLOCK_CACHE, !Initialized); GetMenuBar()->Enable(IDM_JIT_OFF, Pause); diff --git a/Source/Core/DolphinWX/Debugger/CodeWindow.h b/Source/Core/DolphinWX/Debugger/CodeWindow.h index 5788d7d062..0cd6604261 100644 --- a/Source/Core/DolphinWX/Debugger/CodeWindow.h +++ b/Source/Core/DolphinWX/Debugger/CodeWindow.h @@ -4,6 +4,8 @@ #pragma once +#include <array> + #include <wx/aui/framemanager.h> #include <wx/bitmap.h> #include <wx/panel.h> @@ -12,16 +14,16 @@ #include "Common/Event.h" #include "DolphinWX/Globals.h" +class CCodeView; class CFrame; +struct SConfig; +class CBreakPointWindow; class CRegisterWindow; class CWatchWindow; -class CBreakPointWindow; class CMemoryWindow; class CJitWindow; -class CCodeView; class DSPDebuggerLLE; class GFXDebuggerPanel; -struct SConfig; class DolphinAuiToolBar; class wxListBox; @@ -29,6 +31,48 @@ class wxMenu; class wxMenuBar; class wxToolBar; +namespace Details +{ +template <class T> +struct DebugPanelToID; + +template <> +struct DebugPanelToID<CBreakPointWindow> +{ + static constexpr int ID = IDM_BREAKPOINT_WINDOW; +}; +template <> +struct DebugPanelToID<CRegisterWindow> +{ + static constexpr int ID = IDM_REGISTER_WINDOW; +}; +template <> +struct DebugPanelToID<CWatchWindow> +{ + static constexpr int ID = IDM_WATCH_WINDOW; +}; +template <> +struct DebugPanelToID<CMemoryWindow> +{ + static constexpr int ID = IDM_MEMORY_WINDOW; +}; +template <> +struct DebugPanelToID<CJitWindow> +{ + static constexpr int ID = IDM_JIT_WINDOW; +}; +template <> +struct DebugPanelToID<DSPDebuggerLLE> +{ + static constexpr int ID = IDM_SOUND_WINDOW; +}; +template <> +struct DebugPanelToID<GFXDebuggerPanel> +{ + static constexpr int ID = IDM_VIDEO_WINDOW; +}; +} + class CCodeWindow : public wxPanel { public: @@ -41,10 +85,8 @@ public: void Save(); // Parent interaction - CFrame* Parent; wxMenuBar* GetMenuBar(); wxToolBar* GetToolBar(); - wxBitmap m_Bitmaps[Toolbar_Debug_Bitmap_Max]; bool UseInterpreter(); bool BootToPause(); @@ -53,7 +95,7 @@ public: bool JITNoBlockLinking(); bool JumpToAddress(u32 address); - void Update() override; + void Repopulate(); void NotifyMapLoaded(); void CreateMenu(const SConfig& _LocalCoreStartupParameter, wxMenuBar* pMenuBar); void CreateMenuOptions(wxMenu* pMenu); @@ -63,29 +105,35 @@ public: void OpenPages(); // Menu bar - void ToggleCodeWindow(bool bShow); - void ToggleRegisterWindow(bool bShow); - void ToggleWatchWindow(bool bShow); - void ToggleBreakPointWindow(bool bShow); - void ToggleMemoryWindow(bool bShow); - void ToggleJitWindow(bool bShow); - void ToggleSoundWindow(bool bShow); - void ToggleVideoWindow(bool bShow); + // FIXME: This belongs in a separate class. + void TogglePanel(int id, bool show); + wxPanel* GetUntypedPanel(int id) const; + bool HasUntypedPanel(int id) const { return GetUntypedPanel(id) != nullptr; } + template <class T> + T* GetPanel() const + { + return static_cast<T*>(GetUntypedPanel(Details::DebugPanelToID<T>::ID)); + } + template <class T> + bool HasPanel() const + { + return HasUntypedPanel(Details::DebugPanelToID<T>::ID); + } + template <class T> + T* RequirePanel() + { + if (T* p = GetPanel<T>()) + return p; - // Sub dialogs - CRegisterWindow* m_RegisterWindow; - CWatchWindow* m_WatchWindow; - CBreakPointWindow* m_BreakpointWindow; - CMemoryWindow* m_MemoryWindow; - CJitWindow* m_JitWindow; - DSPDebuggerLLE* m_SoundWindow; - GFXDebuggerPanel* m_VideoWindow; + TogglePanel(Details::DebugPanelToID<T>::ID, true); + return GetPanel<T>(); + } // Settings bool bAutomaticStart; bool bBootToPause; - bool bShowOnStart[IDM_VIDEO_WINDOW - IDM_LOG_WINDOW + 1]; - int iNbAffiliation[IDM_CODE_WINDOW - IDM_LOG_WINDOW + 1]; + bool bShowOnStart[IDM_DEBUG_WINDOW_LIST_END - IDM_DEBUG_WINDOW_LIST_START]; + int iNbAffiliation[IDM_DEBUG_WINDOW_LIST_END - IDM_DEBUG_WINDOW_LIST_START]; private: void OnCPUMode(wxCommandEvent& event); @@ -99,7 +147,6 @@ private: void OnProfilerMenu(wxCommandEvent& event); void OnSymbolListChange(wxCommandEvent& event); - void OnSymbolListContextMenu(wxContextMenuEvent& event); void OnCallstackListChange(wxCommandEvent& event); void OnCallersListChange(wxCommandEvent& event); void OnCallsListChange(wxCommandEvent& event); @@ -116,7 +163,15 @@ private: void UpdateCallstack(); void InitBitmaps(); + wxPanel* CreateSiblingPanel(int id); + wxBitmap m_Bitmaps[Toolbar_Debug_Bitmap_Max]; + + // Sibling debugger panels + // FIXME: This obviously belongs in some manager class above this one. + std::array<wxPanel*, IDM_DEBUG_WINDOW_LIST_END - IDM_DEBUG_WINDOW_LIST_START> m_sibling_panels; + + CFrame* Parent; CCodeView* codeview; wxListBox* callstack; wxListBox* symbols; diff --git a/Source/Core/DolphinWX/Debugger/CodeWindowFunctions.cpp b/Source/Core/DolphinWX/Debugger/CodeWindowFunctions.cpp index 0a1300146b..42a4b8f22c 100644 --- a/Source/Core/DolphinWX/Debugger/CodeWindowFunctions.cpp +++ b/Source/Core/DolphinWX/Debugger/CodeWindowFunctions.cpp @@ -453,7 +453,7 @@ void CCodeWindow::OnSymbolsMenu(wxCommandEvent& event) break; case IDM_PATCH_HLE_FUNCTIONS: HLE::PatchFunctions(); - Update(); + Repopulate(); break; } } @@ -464,7 +464,6 @@ void CCodeWindow::NotifyMapLoaded() return; g_symbolDB.FillInCallers(); - // symbols->Show(false); // hide it for faster filling symbols->Freeze(); // HyperIris: wx style fast filling symbols->Clear(); for (const auto& symbol : g_symbolDB.Symbols()) @@ -473,8 +472,7 @@ void CCodeWindow::NotifyMapLoaded() symbols->SetClientData(idx, (void*)&symbol.second); } symbols->Thaw(); - // symbols->Show(true); - Update(); + Repopulate(); } void CCodeWindow::OnSymbolListChange(wxCommandEvent& event) @@ -487,8 +485,9 @@ void CCodeWindow::OnSymbolListChange(wxCommandEvent& event) { if (pSymbol->type == Symbol::Type::Data) { - if (m_MemoryWindow) // && m_MemoryWindow->IsVisible()) - m_MemoryWindow->JumpToAddress(pSymbol->address); + CMemoryWindow* memory = GetPanel<CMemoryWindow>(); + if (memory) + memory->JumpToAddress(pSymbol->address); } else { @@ -498,10 +497,6 @@ void CCodeWindow::OnSymbolListChange(wxCommandEvent& event) } } -void CCodeWindow::OnSymbolListContextMenu(wxContextMenuEvent& event) -{ -} - // Change the global DebuggerFont void CCodeWindow::OnChangeFont(wxCommandEvent& event) { @@ -511,157 +506,104 @@ void CCodeWindow::OnChangeFont(wxCommandEvent& event) wxFontDialog dialog(this, data); if (dialog.ShowModal() == wxID_OK) DebuggerFont = dialog.GetFontData().GetChosenFont(); + + // TODO: Send event to all panels that tells them to reload the font when it changes. } // Toggle windows +wxPanel* CCodeWindow::GetUntypedPanel(int id) const +{ + wxASSERT_MSG(id >= IDM_DEBUG_WINDOW_LIST_START && id < IDM_DEBUG_WINDOW_LIST_END, + "ID out of range"); + wxASSERT_MSG(id != IDM_LOG_WINDOW && id != IDM_LOG_CONFIG_WINDOW, + "Log windows are managed separately"); + return m_sibling_panels.at(id - IDM_DEBUG_WINDOW_LIST_START); +} + +void CCodeWindow::TogglePanel(int id, bool show) +{ + wxPanel* panel = GetUntypedPanel(id); + + // Not all the panels (i.e. CodeWindow) have corresponding menu options. + wxMenuItem* item = GetMenuBar()->FindItem(id); + if (item) + item->Check(show); + + if (show) + { + if (!panel) + { + panel = CreateSiblingPanel(id); + } + Parent->DoAddPage(panel, iNbAffiliation[id - IDM_DEBUG_WINDOW_LIST_START], + Parent->bFloatWindow[id - IDM_DEBUG_WINDOW_LIST_START]); + } + else if (panel) // Close + { + Parent->DoRemovePage(panel, panel == this); + m_sibling_panels[id - IDM_DEBUG_WINDOW_LIST_START] = nullptr; + } +} + +wxPanel* CCodeWindow::CreateSiblingPanel(int id) +{ + // Includes range check inside the get call + wxASSERT_MSG(!GetUntypedPanel(id), "Panel must not already exist"); + + wxPanel* panel = nullptr; + switch (id) + { + // case IDM_LOG_WINDOW: // These exist separately in CFrame. + // case IDM_LOG_CONFIG_WINDOW: + case IDM_REGISTER_WINDOW: + panel = new CRegisterWindow(Parent, IDM_REGISTER_WINDOW); + break; + case IDM_WATCH_WINDOW: + panel = new CWatchWindow(Parent, IDM_WATCH_WINDOW); + break; + case IDM_BREAKPOINT_WINDOW: + panel = new CBreakPointWindow(this, Parent, IDM_BREAKPOINT_WINDOW); + break; + case IDM_MEMORY_WINDOW: + panel = new CMemoryWindow(Parent, IDM_MEMORY_WINDOW); + break; + case IDM_JIT_WINDOW: + panel = new CJitWindow(Parent, IDM_JIT_WINDOW); + break; + case IDM_SOUND_WINDOW: + panel = new DSPDebuggerLLE(Parent, IDM_SOUND_WINDOW); + break; + case IDM_VIDEO_WINDOW: + panel = new GFXDebuggerPanel(Parent, IDM_VIDEO_WINDOW); + break; + case IDM_CODE_WINDOW: + panel = this; + break; + default: + wxTrap(); + break; + } + + m_sibling_panels[id - IDM_DEBUG_WINDOW_LIST_START] = panel; + return panel; +} + void CCodeWindow::OpenPages() { - ToggleCodeWindow(true); - if (bShowOnStart[0]) + // This is forced, and should always be placed as the first tab in the notebook. + TogglePanel(IDM_CODE_WINDOW, true); + + // These panels are managed separately by CFrame + if (bShowOnStart[IDM_LOG_WINDOW - IDM_DEBUG_WINDOW_LIST_START]) Parent->ToggleLogWindow(true); - if (bShowOnStart[IDM_LOG_CONFIG_WINDOW - IDM_LOG_WINDOW]) + if (bShowOnStart[IDM_LOG_CONFIG_WINDOW - IDM_DEBUG_WINDOW_LIST_START]) Parent->ToggleLogConfigWindow(true); - if (bShowOnStart[IDM_REGISTER_WINDOW - IDM_LOG_WINDOW]) - ToggleRegisterWindow(true); - if (bShowOnStart[IDM_WATCH_WINDOW - IDM_LOG_WINDOW]) - ToggleWatchWindow(true); - if (bShowOnStart[IDM_BREAKPOINT_WINDOW - IDM_LOG_WINDOW]) - ToggleBreakPointWindow(true); - if (bShowOnStart[IDM_MEMORY_WINDOW - IDM_LOG_WINDOW]) - ToggleMemoryWindow(true); - if (bShowOnStart[IDM_JIT_WINDOW - IDM_LOG_WINDOW]) - ToggleJitWindow(true); - if (bShowOnStart[IDM_SOUND_WINDOW - IDM_LOG_WINDOW]) - ToggleSoundWindow(true); - if (bShowOnStart[IDM_VIDEO_WINDOW - IDM_LOG_WINDOW]) - ToggleVideoWindow(true); -} -void CCodeWindow::ToggleCodeWindow(bool bShow) -{ - if (bShow) - Parent->DoAddPage(this, iNbAffiliation[IDM_CODE_WINDOW - IDM_LOG_WINDOW], - Parent->bFloatWindow[IDM_CODE_WINDOW - IDM_LOG_WINDOW]); - else // Hide - Parent->DoRemovePage(this); -} - -void CCodeWindow::ToggleRegisterWindow(bool bShow) -{ - GetMenuBar()->FindItem(IDM_REGISTER_WINDOW)->Check(bShow); - if (bShow) + // Iterate normal panels that don't have weird rules. + for (int i = IDM_REGISTER_WINDOW; i < IDM_CODE_WINDOW; ++i) { - if (!m_RegisterWindow) - m_RegisterWindow = new CRegisterWindow(Parent, IDM_REGISTER_WINDOW); - Parent->DoAddPage(m_RegisterWindow, iNbAffiliation[IDM_REGISTER_WINDOW - IDM_LOG_WINDOW], - Parent->bFloatWindow[IDM_REGISTER_WINDOW - IDM_LOG_WINDOW]); - } - else // Close - { - Parent->DoRemovePage(m_RegisterWindow, false); - m_RegisterWindow = nullptr; - } -} - -void CCodeWindow::ToggleWatchWindow(bool bShow) -{ - GetMenuBar()->FindItem(IDM_WATCH_WINDOW)->Check(bShow); - if (bShow) - { - if (!m_WatchWindow) - m_WatchWindow = new CWatchWindow(Parent, IDM_WATCH_WINDOW); - Parent->DoAddPage(m_WatchWindow, iNbAffiliation[IDM_WATCH_WINDOW - IDM_LOG_WINDOW], - Parent->bFloatWindow[IDM_WATCH_WINDOW - IDM_LOG_WINDOW]); - } - else // Close - { - Parent->DoRemovePage(m_WatchWindow, false); - m_WatchWindow = nullptr; - } -} - -void CCodeWindow::ToggleBreakPointWindow(bool bShow) -{ - GetMenuBar()->FindItem(IDM_BREAKPOINT_WINDOW)->Check(bShow); - if (bShow) - { - if (!m_BreakpointWindow) - m_BreakpointWindow = new CBreakPointWindow(this, Parent, IDM_BREAKPOINT_WINDOW); - Parent->DoAddPage(m_BreakpointWindow, iNbAffiliation[IDM_BREAKPOINT_WINDOW - IDM_LOG_WINDOW], - Parent->bFloatWindow[IDM_BREAKPOINT_WINDOW - IDM_LOG_WINDOW]); - } - else // Close - { - Parent->DoRemovePage(m_BreakpointWindow, false); - m_BreakpointWindow = nullptr; - } -} - -void CCodeWindow::ToggleMemoryWindow(bool bShow) -{ - GetMenuBar()->FindItem(IDM_MEMORY_WINDOW)->Check(bShow); - if (bShow) - { - if (!m_MemoryWindow) - m_MemoryWindow = new CMemoryWindow(this, Parent, IDM_MEMORY_WINDOW); - Parent->DoAddPage(m_MemoryWindow, iNbAffiliation[IDM_MEMORY_WINDOW - IDM_LOG_WINDOW], - Parent->bFloatWindow[IDM_MEMORY_WINDOW - IDM_LOG_WINDOW]); - } - else // Close - { - Parent->DoRemovePage(m_MemoryWindow, false); - m_MemoryWindow = nullptr; - } -} - -void CCodeWindow::ToggleJitWindow(bool bShow) -{ - GetMenuBar()->FindItem(IDM_JIT_WINDOW)->Check(bShow); - if (bShow) - { - if (!m_JitWindow) - m_JitWindow = new CJitWindow(Parent, IDM_JIT_WINDOW); - Parent->DoAddPage(m_JitWindow, iNbAffiliation[IDM_JIT_WINDOW - IDM_LOG_WINDOW], - Parent->bFloatWindow[IDM_JIT_WINDOW - IDM_LOG_WINDOW]); - } - else // Close - { - Parent->DoRemovePage(m_JitWindow, false); - m_JitWindow = nullptr; - } -} - -void CCodeWindow::ToggleSoundWindow(bool bShow) -{ - GetMenuBar()->FindItem(IDM_SOUND_WINDOW)->Check(bShow); - if (bShow) - { - if (!m_SoundWindow) - m_SoundWindow = new DSPDebuggerLLE(Parent, IDM_SOUND_WINDOW); - Parent->DoAddPage(m_SoundWindow, iNbAffiliation[IDM_SOUND_WINDOW - IDM_LOG_WINDOW], - Parent->bFloatWindow[IDM_SOUND_WINDOW - IDM_LOG_WINDOW]); - } - else // Close - { - Parent->DoRemovePage(m_SoundWindow, false); - m_SoundWindow = nullptr; - } -} - -void CCodeWindow::ToggleVideoWindow(bool bShow) -{ - GetMenuBar()->FindItem(IDM_VIDEO_WINDOW)->Check(bShow); - if (bShow) - { - if (!m_VideoWindow) - m_VideoWindow = new GFXDebuggerPanel(Parent, IDM_VIDEO_WINDOW); - Parent->DoAddPage(m_VideoWindow, iNbAffiliation[IDM_VIDEO_WINDOW - IDM_LOG_WINDOW], - Parent->bFloatWindow[IDM_VIDEO_WINDOW - IDM_LOG_WINDOW]); - } - else // Close - { - Parent->DoRemovePage(m_VideoWindow, false); - m_VideoWindow = nullptr; + if (bShowOnStart[i - IDM_DEBUG_WINDOW_LIST_START]) + TogglePanel(i, true); } } diff --git a/Source/Core/DolphinWX/Debugger/DSPDebugWindow.cpp b/Source/Core/DolphinWX/Debugger/DSPDebugWindow.cpp index 199f1d376e..69301fc233 100644 --- a/Source/Core/DolphinWX/Debugger/DSPDebugWindow.cpp +++ b/Source/Core/DolphinWX/Debugger/DSPDebugWindow.cpp @@ -32,9 +32,8 @@ static DSPDebuggerLLE* m_DebuggerFrame = nullptr; DSPDebuggerLLE::DSPDebuggerLLE(wxWindow* parent, wxWindowID id) : wxPanel(parent, id, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _("DSP LLE Debugger")), - m_CachedStepCounter(-1) + m_CachedStepCounter(-1), m_toolbar_item_size(FromDIP(wxSize(16, 16))) { - Bind(wxEVT_CLOSE_WINDOW, &DSPDebuggerLLE::OnClose, this); Bind(wxEVT_MENU, &DSPDebuggerLLE::OnChangeState, this, ID_RUNTOOL, ID_SHOWPCTOOL); m_DebuggerFrame = this; @@ -46,11 +45,12 @@ DSPDebuggerLLE::DSPDebuggerLLE(wxWindow* parent, wxWindowID id) m_Toolbar = new DolphinAuiToolBar(this, ID_TOOLBAR, wxDefaultPosition, wxDefaultSize, wxAUI_TB_HORZ_TEXT); m_Toolbar->AddTool(ID_RUNTOOL, _("Pause"), - wxArtProvider::GetBitmap(wxART_TICK_MARK, wxART_OTHER, wxSize(10, 10))); + wxArtProvider::GetBitmap(wxART_TICK_MARK, wxART_OTHER, m_toolbar_item_size)); m_Toolbar->AddTool(ID_STEPTOOL, _("Step"), - wxArtProvider::GetBitmap(wxART_GO_DOWN, wxART_OTHER, wxSize(10, 10))); - m_Toolbar->AddTool(ID_SHOWPCTOOL, _("Show PC"), - wxArtProvider::GetBitmap(wxART_GO_TO_PARENT, wxART_OTHER, wxSize(10, 10))); + wxArtProvider::GetBitmap(wxART_GO_DOWN, wxART_OTHER, m_toolbar_item_size)); + m_Toolbar->AddTool( + ID_SHOWPCTOOL, _("Show PC"), + wxArtProvider::GetBitmap(wxART_GO_TO_PARENT, wxART_OTHER, m_toolbar_item_size)); m_Toolbar->AddSeparator(); m_addr_txtctrl = new wxTextCtrl(m_Toolbar, wxID_ANY, wxEmptyString, wxDefaultPosition, @@ -60,8 +60,8 @@ DSPDebuggerLLE::DSPDebuggerLLE(wxWindow* parent, wxWindowID id) m_Toolbar->AddControl(m_addr_txtctrl); m_Toolbar->Realize(); - m_SymbolList = - new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(140, 100), 0, nullptr, wxLB_SORT); + m_SymbolList = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDLG_UNIT(this, wxSize(100, 80)), + 0, nullptr, wxLB_SORT); m_SymbolList->Bind(wxEVT_LISTBOX, &DSPDebuggerLLE::OnSymbolListChange, this); m_MainNotebook = new wxAuiNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, @@ -71,15 +71,14 @@ DSPDebuggerLLE::DSPDebuggerLLE(wxWindow* parent, wxWindowID id) wxBoxSizer* code_sizer = new wxBoxSizer(wxVERTICAL); m_CodeView = new CCodeView(&debug_interface, &DSPSymbols::g_dsp_symbol_db, code_panel); m_CodeView->SetPlain(); - code_sizer->Add(m_CodeView, 1, wxALL | wxEXPAND); + code_sizer->Add(m_CodeView, 1, wxEXPAND); code_panel->SetSizer(code_sizer); m_MainNotebook->AddPage(code_panel, _("Disassembly"), true); wxPanel* mem_panel = new wxPanel(m_MainNotebook, wxID_ANY); wxBoxSizer* mem_sizer = new wxBoxSizer(wxVERTICAL); - // TODO insert memViewer class m_MemView = new CMemoryView(&debug_interface, mem_panel); - mem_sizer->Add(m_MemView, 1, wxALL | wxEXPAND); + mem_sizer->Add(m_MemView, 1, wxEXPAND); mem_panel->SetSizer(mem_sizer); m_MainNotebook->AddPage(mem_panel, _("Memory")); @@ -111,11 +110,6 @@ DSPDebuggerLLE::~DSPDebuggerLLE() m_DebuggerFrame = nullptr; } -void DSPDebuggerLLE::OnClose(wxCloseEvent& event) -{ - event.Skip(); -} - void DSPDebuggerLLE::OnChangeState(wxCommandEvent& event) { if (DSPCore_GetState() == DSPCORE_STOP) @@ -134,7 +128,7 @@ void DSPDebuggerLLE::OnChangeState(wxCommandEvent& event) if (DSPCore_GetState() == DSPCORE_STEPPING) { DSPCore_Step(); - Update(); + Repopulate(); } break; @@ -149,24 +143,24 @@ void DSPDebuggerLLE::OnChangeState(wxCommandEvent& event) void Host_RefreshDSPDebuggerWindow() { + // FIXME: This should use QueueEvent to post the update request to the UI thread. + // Need to check if this can safely be performed asynchronously or if it races. + // FIXME: This probably belongs in Main.cpp with the other host functions. + // NOTE: The DSP never tells us when it shuts down. It probably should. if (m_DebuggerFrame) - m_DebuggerFrame->Update(); + m_DebuggerFrame->Repopulate(); } -void DSPDebuggerLLE::Update() +void DSPDebuggerLLE::Repopulate() { -#if defined __WXGTK__ if (!wxIsMainThread()) wxMutexGuiEnter(); -#endif UpdateSymbolMap(); UpdateDisAsmListView(); UpdateRegisterFlags(); UpdateState(); -#if defined __WXGTK__ if (!wxIsMainThread()) wxMutexGuiLeave(); -#endif } void DSPDebuggerLLE::FocusOnPC() @@ -180,14 +174,14 @@ void DSPDebuggerLLE::UpdateState() { m_Toolbar->SetToolLabel(ID_RUNTOOL, _("Pause")); m_Toolbar->SetToolBitmap( - ID_RUNTOOL, wxArtProvider::GetBitmap(wxART_TICK_MARK, wxART_OTHER, wxSize(10, 10))); + ID_RUNTOOL, wxArtProvider::GetBitmap(wxART_TICK_MARK, wxART_OTHER, m_toolbar_item_size)); m_Toolbar->EnableTool(ID_STEPTOOL, false); } else { m_Toolbar->SetToolLabel(ID_RUNTOOL, _("Run")); m_Toolbar->SetToolBitmap( - ID_RUNTOOL, wxArtProvider::GetBitmap(wxART_GO_FORWARD, wxART_OTHER, wxSize(10, 10))); + ID_RUNTOOL, wxArtProvider::GetBitmap(wxART_GO_FORWARD, wxART_OTHER, m_toolbar_item_size)); m_Toolbar->EnableTool(ID_STEPTOOL, true); } m_Toolbar->Realize(); @@ -201,7 +195,7 @@ void DSPDebuggerLLE::UpdateDisAsmListView() // show PC FocusOnPC(); m_CachedStepCounter = g_dsp.step_counter; - m_Regs->Update(); + m_Regs->Repopulate(); } void DSPDebuggerLLE::UpdateSymbolMap() diff --git a/Source/Core/DolphinWX/Debugger/DSPDebugWindow.h b/Source/Core/DolphinWX/Debugger/DSPDebugWindow.h index 51519f010c..c67f2f7682 100644 --- a/Source/Core/DolphinWX/Debugger/DSPDebugWindow.h +++ b/Source/Core/DolphinWX/Debugger/DSPDebugWindow.h @@ -23,7 +23,7 @@ public: DSPDebuggerLLE(wxWindow* parent, wxWindowID id = wxID_ANY); virtual ~DSPDebuggerLLE(); - void Update() override; + void Repopulate(); private: enum @@ -52,8 +52,8 @@ private: wxListBox* m_SymbolList; wxTextCtrl* m_addr_txtctrl; wxAuiNotebook* m_MainNotebook; + wxSize m_toolbar_item_size; - void OnClose(wxCloseEvent& event); void OnChangeState(wxCommandEvent& event); // void OnRightClick(wxListEvent& event); // void OnDoubleClick(wxListEvent& event); diff --git a/Source/Core/DolphinWX/Debugger/DSPRegisterView.cpp b/Source/Core/DolphinWX/Debugger/DSPRegisterView.cpp index 5caa406d25..4f0d6fb2d3 100644 --- a/Source/Core/DolphinWX/Debugger/DSPRegisterView.cpp +++ b/Source/Core/DolphinWX/Debugger/DSPRegisterView.cpp @@ -71,7 +71,7 @@ wxGridCellAttr* CDSPRegTable::GetAttr(int row, int col, wxGridCellAttr::wxAttrKi } DSPRegisterView::DSPRegisterView(wxWindow* parent, wxWindowID id) - : wxGrid(parent, id, wxDefaultPosition, wxSize(130, 120)) + : wxGrid(parent, id, wxDefaultPosition, wxDLG_UNIT(parent, wxSize(100, 80))) { m_register_table = new CDSPRegTable(); @@ -83,7 +83,7 @@ DSPRegisterView::DSPRegisterView(wxWindow* parent, wxWindowID id) AutoSizeColumns(); } -void DSPRegisterView::Update() +void DSPRegisterView::Repopulate() { m_register_table->UpdateCachedRegs(); ForceRefresh(); diff --git a/Source/Core/DolphinWX/Debugger/DSPRegisterView.h b/Source/Core/DolphinWX/Debugger/DSPRegisterView.h index a8ad3fd60d..dccfb7f975 100644 --- a/Source/Core/DolphinWX/Debugger/DSPRegisterView.h +++ b/Source/Core/DolphinWX/Debugger/DSPRegisterView.h @@ -38,7 +38,7 @@ class DSPRegisterView : public wxGrid { public: DSPRegisterView(wxWindow* parent, wxWindowID id = wxID_ANY); - void Update() override; + void Repopulate(); private: // Owned by wx. Deleted implicitly upon destruction. diff --git a/Source/Core/DolphinWX/Debugger/DebuggerPanel.cpp b/Source/Core/DolphinWX/Debugger/DebuggerPanel.cpp index c1a6a90eca..b8a909bf85 100644 --- a/Source/Core/DolphinWX/Debugger/DebuggerPanel.cpp +++ b/Source/Core/DolphinWX/Debugger/DebuggerPanel.cpp @@ -11,6 +11,7 @@ #include <wx/sizer.h> #include <wx/textctrl.h> +#include "Common/CommonFuncs.h" #include "Common/FileUtil.h" #include "Common/IniFile.h" #include "Core/ConfigManager.h" @@ -26,10 +27,6 @@ GFXDebuggerPanel::GFXDebuggerPanel(wxWindow* parent, wxWindowID id, const wxPoin g_pdebugger = this; CreateGUIControls(); - - Bind(wxEVT_CLOSE_WINDOW, &GFXDebuggerPanel::OnClose, this); - - LoadSettings(); } GFXDebuggerPanel::~GFXDebuggerPanel() @@ -38,55 +35,6 @@ GFXDebuggerPanel::~GFXDebuggerPanel() GFXDebuggerPauseFlag = false; } -void GFXDebuggerPanel::OnClose(wxCloseEvent& event) -{ - // save the window position when we hide the window - SaveSettings(); - - event.Skip(); -} - -void GFXDebuggerPanel::SaveSettings() const -{ - IniFile file; - file.Load(File::GetUserPath(F_DEBUGGERCONFIG_IDX)); - - // TODO: make this work when we close the entire program too, currently on total close we get - // weird values, perhaps because of some conflict with the rendering window - - // TODO: get the screen resolution and make limits from that - if (GetPosition().x < 1000 && GetPosition().y < 1000 && GetSize().GetWidth() < 1000 && - GetSize().GetHeight() < 1000) - { - IniFile::Section* video_window = file.GetOrCreateSection("VideoWindow"); - video_window->Set("x", GetPosition().x); - video_window->Set("y", GetPosition().y); - video_window->Set("w", GetSize().GetWidth()); - video_window->Set("h", GetSize().GetHeight()); - } - - file.Save(File::GetUserPath(F_DEBUGGERCONFIG_IDX)); -} - -void GFXDebuggerPanel::LoadSettings() -{ - IniFile file; - file.Load(File::GetUserPath(F_DEBUGGERCONFIG_IDX)); - - int x = 100; - int y = 100; - int w = 100; - int h = 100; - - IniFile::Section* video_window = file.GetOrCreateSection("VideoWindow"); - video_window->Get("x", &x, GetPosition().x); - video_window->Get("y", &y, GetPosition().y); - video_window->Get("w", &w, GetSize().GetWidth()); - video_window->Get("h", &h, GetSize().GetHeight()); - - SetSize(x, y, w, h); -} - struct PauseEventMap { PauseEvent event; @@ -118,10 +66,9 @@ void GFXDebuggerPanel::CreateGUIControls() {NEXT_ERROR, _("Error")}}; pauseEventMap = map; - const int numPauseEventMap = sizeof(map) / sizeof(PauseEventMap); + static constexpr int numPauseEventMap = ArraySize(map); - // Basic settings - CenterOnParent(); + const int space3 = FromDIP(3); m_pButtonPause = new wxButton(this, wxID_ANY, _("Pause"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _("Pause")); @@ -139,10 +86,11 @@ void GFXDebuggerPanel::CreateGUIControls() wxDefaultValidator, _("Continue")); m_pButtonCont->Bind(wxEVT_BUTTON, &GFXDebuggerPanel::OnContButton, this); - m_pCount = new wxTextCtrl(this, wxID_ANY, "1", wxDefaultPosition, wxSize(50, 25), wxTE_RIGHT, + m_pCount = new wxTextCtrl(this, wxID_ANY, "1", wxDefaultPosition, wxDefaultSize, wxTE_RIGHT, wxDefaultValidator, _("Count")); + m_pCount->SetMinSize(WxUtils::GetTextWidgetMinSize(m_pCount, 10000)); - m_pPauseAtList = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxSize(100, 25), 0, nullptr, 0, + m_pPauseAtList = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, nullptr, 0, wxDefaultValidator, _("PauseAtList")); for (int i = 0; i < numPauseEventMap; i++) { @@ -180,7 +128,7 @@ void GFXDebuggerPanel::CreateGUIControls() m_pButtonClearPixelShaderCache->Bind(wxEVT_BUTTON, &GFXDebuggerPanel::OnClearPixelShaderCacheButton, this); - m_pDumpList = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxSize(120, 25), 0, nullptr, 0, + m_pDumpList = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, nullptr, 0, wxDefaultValidator, _("DumpList")); m_pDumpList->Insert(_("Pixel Shader"), 0); m_pDumpList->Append(_("Vertex Shader")); @@ -196,31 +144,40 @@ void GFXDebuggerPanel::CreateGUIControls() wxBoxSizer* sMain = new wxBoxSizer(wxVERTICAL); - wxStaticBoxSizer* const pFlowCtrlBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Flow Control")); wxBoxSizer* const pPauseAtNextSzr = new wxBoxSizer(wxHORIZONTAL); - pFlowCtrlBox->Add(m_pButtonPause); - pPauseAtNextSzr->Add(m_pButtonPauseAtNext); - pPauseAtNextSzr->Add(m_pCount); - pPauseAtNextSzr->Add(m_pPauseAtList); - pFlowCtrlBox->Add(pPauseAtNextSzr); - pFlowCtrlBox->Add(m_pButtonPauseAtNextFrame); - pFlowCtrlBox->Add(m_pButtonCont); + pPauseAtNextSzr->Add(m_pCount, 0, wxALIGN_CENTER_VERTICAL); + pPauseAtNextSzr->Add(m_pPauseAtList, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space3); + + wxFlexGridSizer* const flow_szr = new wxFlexGridSizer(2, space3, space3); + flow_szr->Add(m_pButtonPause, 0, wxEXPAND); + flow_szr->AddSpacer(1); + flow_szr->Add(m_pButtonPauseAtNext, 0, wxEXPAND); + flow_szr->Add(pPauseAtNextSzr, 0, wxEXPAND); + flow_szr->Add(m_pButtonPauseAtNextFrame, 0, wxEXPAND); + flow_szr->AddSpacer(1); + flow_szr->Add(m_pButtonCont, 0, wxEXPAND); + flow_szr->AddSpacer(1); + + wxStaticBoxSizer* const pFlowCtrlBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Flow Control")); + pFlowCtrlBox->Add(flow_szr, 1, wxEXPAND); + + wxBoxSizer* const pDumpSzr = new wxBoxSizer(wxHORIZONTAL); + pDumpSzr->Add(m_pButtonDump, 0, wxALIGN_CENTER_VERTICAL); + pDumpSzr->Add(m_pDumpList, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space3); + + wxGridSizer* const pDbgGrid = new wxGridSizer(2, space3, space3); + pDbgGrid->Add(m_pButtonUpdateScreen, 0, wxEXPAND); + pDbgGrid->Add(m_pButtonClearScreen, 0, wxEXPAND); + pDbgGrid->Add(m_pButtonClearTextureCache, 0, wxEXPAND); + pDbgGrid->Add(m_pButtonClearVertexShaderCache, 0, wxEXPAND); + pDbgGrid->Add(m_pButtonClearPixelShaderCache, 0, wxEXPAND); wxStaticBoxSizer* const pDebugBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Debugging")); - wxBoxSizer* const pDumpSzr = new wxBoxSizer(wxHORIZONTAL); - pDumpSzr->Add(m_pButtonDump); - pDumpSzr->Add(m_pDumpList); - pDebugBox->Add(pDumpSzr); - wxGridSizer* const pDbgGrid = new wxGridSizer(2, 5, 5); - pDbgGrid->Add(m_pButtonUpdateScreen); - pDbgGrid->Add(m_pButtonClearScreen); - pDbgGrid->Add(m_pButtonClearTextureCache); - pDbgGrid->Add(m_pButtonClearVertexShaderCache); - pDbgGrid->Add(m_pButtonClearPixelShaderCache); - pDebugBox->Add(pDbgGrid); + pDebugBox->Add(pDumpSzr, 0, wxEXPAND); + pDebugBox->Add(pDbgGrid, 1, wxTOP, space3); - sMain->Add(pFlowCtrlBox, 0, 0, 5); - sMain->Add(pDebugBox, 0, 0, 5); + sMain->Add(pFlowCtrlBox); + sMain->Add(pDebugBox); SetSizerAndFit(sMain); OnContinue(); @@ -248,12 +205,6 @@ void GFXDebuggerPanel::OnContinue() m_pButtonClearPixelShaderCache->Disable(); } -// General settings -void GFXDebuggerPanel::GeneralSettings(wxCommandEvent& event) -{ - SaveSettings(); -} - void GFXDebuggerPanel::OnPauseButton(wxCommandEvent& event) { GFXDebuggerPauseFlag = true; diff --git a/Source/Core/DolphinWX/Debugger/DebuggerPanel.h b/Source/Core/DolphinWX/Debugger/DebuggerPanel.h index cc30037366..3e9864cd6c 100644 --- a/Source/Core/DolphinWX/Debugger/DebuggerPanel.h +++ b/Source/Core/DolphinWX/Debugger/DebuggerPanel.h @@ -20,9 +20,6 @@ public: virtual ~GFXDebuggerPanel(); - void SaveSettings() const; - void LoadSettings(); - bool bInfoLog; bool bPrimLog; bool bSaveTextures; @@ -49,11 +46,8 @@ private: wxButton* m_pButtonClearPixelShaderCache; wxTextCtrl* m_pCount; - void OnClose(wxCloseEvent& event); void CreateGUIControls(); - void GeneralSettings(wxCommandEvent& event); - // These set GFXDebuggerPauseFlag to true (either immediately or once the specified event has // occurred) void OnPauseButton(wxCommandEvent& event); diff --git a/Source/Core/DolphinWX/Debugger/JitWindow.cpp b/Source/Core/DolphinWX/Debugger/JitWindow.cpp index 4bc9dfedb9..79f1daffe4 100644 --- a/Source/Core/DolphinWX/Debugger/JitWindow.cpp +++ b/Source/Core/DolphinWX/Debugger/JitWindow.cpp @@ -34,7 +34,8 @@ CJitWindow::CJitWindow(wxWindow* parent, wxWindowID id, const wxPoint& pos, cons sizerSplit->Add(x86_box = new wxTextCtrl(this, wxID_ANY, "(x86)", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE), 1, wxEXPAND); - sizerBig->Add(block_list = new JitBlockList(this, wxID_ANY, wxDefaultPosition, wxSize(100, 140), + sizerBig->Add(block_list = new JitBlockList(this, wxID_ANY, wxDefaultPosition, + wxDLG_UNIT(this, wxSize(80, 96)), wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT | wxLC_SINGLE_SEL | wxLC_SORT_ASCENDING), 0, wxEXPAND); @@ -43,10 +44,7 @@ CJitWindow::CJitWindow(wxWindow* parent, wxWindowID id, const wxPoint& pos, cons sizerBig->Add(button_refresh = new wxButton(this, wxID_ANY, _("&Refresh"))); button_refresh->Bind(wxEVT_BUTTON, &CJitWindow::OnRefresh, this); - SetSizer(sizerBig); - - sizerSplit->Fit(this); - sizerBig->Fit(this); + SetSizerAndFit(sizerBig); #if defined(_M_X86) m_disassembler.reset(GetNewDisassembler("x86")); @@ -59,7 +57,7 @@ CJitWindow::CJitWindow(wxWindow* parent, wxWindowID id, const wxPoint& pos, cons void CJitWindow::OnRefresh(wxCommandEvent& /*event*/) { - block_list->Update(); + block_list->Repopulate(); } void CJitWindow::ViewAddr(u32 em_address) @@ -135,7 +133,7 @@ void CJitWindow::Compare(u32 em_address) } } -void CJitWindow::Update() +void CJitWindow::Repopulate() { } @@ -181,6 +179,6 @@ void JitBlockList::Init() InsertColumn(COLUMN_COST, _("Cost")); } -void JitBlockList::Update() +void JitBlockList::Repopulate() { } diff --git a/Source/Core/DolphinWX/Debugger/JitWindow.h b/Source/Core/DolphinWX/Debugger/JitWindow.h index 5bfd31d0a8..aa647efe8b 100644 --- a/Source/Core/DolphinWX/Debugger/JitWindow.h +++ b/Source/Core/DolphinWX/Debugger/JitWindow.h @@ -22,7 +22,7 @@ public: JitBlockList(wxWindow* parent, const wxWindowID id, const wxPoint& pos, const wxSize& size, long style); void Init(); - void Update() override; + void Repopulate(); }; class CJitWindow : public wxPanel @@ -33,7 +33,7 @@ public: const wxString& name = _("JIT Block Viewer")); void ViewAddr(u32 em_address); - void Update() override; + void Repopulate(); private: void OnRefresh(wxCommandEvent& /*event*/); diff --git a/Source/Core/DolphinWX/Debugger/MemoryCheckDlg.cpp b/Source/Core/DolphinWX/Debugger/MemoryCheckDlg.cpp index 0f8f1263f9..51cfc7b462 100644 --- a/Source/Core/DolphinWX/Debugger/MemoryCheckDlg.cpp +++ b/Source/Core/DolphinWX/Debugger/MemoryCheckDlg.cpp @@ -22,6 +22,8 @@ MemoryCheckDlg::MemoryCheckDlg(CBreakPointWindow* parent) Bind(wxEVT_BUTTON, &MemoryCheckDlg::OnOK, this, wxID_OK); Bind(wxEVT_RADIOBUTTON, &MemoryCheckDlg::OnRadioButtonClick, this); + const int space5 = FromDIP(5); + m_textAddress = new wxStaticText(this, wxID_ANY, _("Address")); m_textStartAddress = new wxStaticText(this, wxID_ANY, _("Start")); m_textStartAddress->Disable(); @@ -46,14 +48,22 @@ MemoryCheckDlg::MemoryCheckDlg(CBreakPointWindow* parent) m_radioBreakLog->SetValue(true); auto* sAddressBox = new wxBoxSizer(wxHORIZONTAL); - sAddressBox->Add(m_textAddress, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - sAddressBox->Add(m_pEditAddress, 1, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 10); + sAddressBox->AddSpacer(space5); + sAddressBox->Add(m_textAddress, 0, wxALIGN_CENTER_VERTICAL); + sAddressBox->AddSpacer(space5); + sAddressBox->Add(m_pEditAddress, 1, wxALIGN_CENTER_VERTICAL); + sAddressBox->AddSpacer(space5); auto* sAddressRangeBox = new wxBoxSizer(wxHORIZONTAL); - sAddressRangeBox->Add(m_textStartAddress, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - sAddressRangeBox->Add(m_pEditStartAddress, 1, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 10); - sAddressRangeBox->Add(m_textEndAddress, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - sAddressRangeBox->Add(m_pEditEndAddress, 1, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 5); + sAddressRangeBox->AddSpacer(space5); + sAddressRangeBox->Add(m_textStartAddress, 0, wxALIGN_CENTER_VERTICAL); + sAddressRangeBox->AddSpacer(space5); + sAddressRangeBox->Add(m_pEditStartAddress, 1, wxALIGN_CENTER_VERTICAL); + sAddressRangeBox->AddSpacer(space5); + sAddressRangeBox->Add(m_textEndAddress, 0, wxALIGN_CENTER_VERTICAL); + sAddressRangeBox->AddSpacer(space5); + sAddressRangeBox->Add(m_pEditEndAddress, 1, wxALIGN_CENTER_VERTICAL); + sAddressRangeBox->AddSpacer(space5); auto* sActions = new wxStaticBoxSizer(wxVERTICAL, this, _("Action")); sActions->Add(m_radioRead, 0, wxEXPAND); @@ -67,19 +77,26 @@ MemoryCheckDlg::MemoryCheckDlg(CBreakPointWindow* parent) sFlags->Add(m_radioBreakLog); auto* sOptionsBox = new wxBoxSizer(wxHORIZONTAL); - sOptionsBox->Add(sActions, 1, wxEXPAND | wxRIGHT | wxTOP | wxBOTTOM, 5); - sOptionsBox->Add(sFlags, 1, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 5); + sOptionsBox->Add(sActions, 1, wxEXPAND, space5); + sOptionsBox->Add(sFlags, 1, wxEXPAND | wxLEFT, space5); auto* sControls = new wxBoxSizer(wxVERTICAL); sControls->Add(m_radioAddress, 0, wxEXPAND); + sControls->AddSpacer(5); sControls->Add(sAddressBox, 0, wxEXPAND); + sControls->AddSpacer(5); sControls->Add(m_radioRange, 0, wxEXPAND); + sControls->AddSpacer(5); sControls->Add(sAddressRangeBox, 0, wxEXPAND); + sControls->AddSpacer(5); sControls->Add(sOptionsBox, 0, wxEXPAND); auto* sMainSizer = new wxBoxSizer(wxVERTICAL); - sMainSizer->Add(sControls, 0, wxEXPAND | wxTOP | wxRIGHT | wxLEFT, 5); - sMainSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + sMainSizer->AddSpacer(space5); + sMainSizer->Add(sControls, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sMainSizer->AddSpacer(space5); + sMainSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sMainSizer->AddSpacer(space5); SetSizerAndFit(sMainSizer); SetFocus(); diff --git a/Source/Core/DolphinWX/Debugger/MemoryView.cpp b/Source/Core/DolphinWX/Debugger/MemoryView.cpp index f5bebea901..47c2969dc2 100644 --- a/Source/Core/DolphinWX/Debugger/MemoryView.cpp +++ b/Source/Core/DolphinWX/Debugger/MemoryView.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include <algorithm> +#include <cctype> #include <cmath> #include <string> #include <wx/brush.h> @@ -17,6 +19,7 @@ #include "Common/CommonTypes.h" #include "Common/DebugInterface.h" #include "Common/StringUtil.h" +#include "Core/HW/Memmap.h" #include "Core/PowerPC/PPCSymbolDB.h" #include "Core/PowerPC/PowerPC.h" #include "DolphinWX/Debugger/CodeWindow.h" @@ -25,6 +28,7 @@ #include "DolphinWX/Debugger/WatchWindow.h" #include "DolphinWX/Frame.h" #include "DolphinWX/Globals.h" +#include "DolphinWX/Main.h" #include "DolphinWX/WxUtils.h" enum @@ -42,11 +46,12 @@ enum IDM_VIEWASHEX, }; +wxDEFINE_EVENT(DOLPHIN_EVT_MEMORY_VIEW_DATA_TYPE_CHANGED, wxCommandEvent); + CMemoryView::CMemoryView(DebugInterface* debuginterface, wxWindow* parent) - : wxControl(parent, wxID_ANY), debugger(debuginterface), - align(debuginterface->GetInstructionSize(0)), rowHeight(13), selection(0), oldSelection(0), - selecting(false), memory(0), curAddress(debuginterface->GetPC()), - dataType(MemoryDataType::U8), viewAsType(VIEWAS_FP) + : wxControl(parent, wxID_ANY), debugger(debuginterface), align(0), rowHeight(FromDIP(13)), + m_left_col_width(FromDIP(LEFT_COL_WIDTH)), selection(0), oldSelection(0), selecting(false), + memory(0), curAddress(debuginterface->GetPC()), m_data_type(MemoryDataType::U8) { Bind(wxEVT_PAINT, &CMemoryView::OnPaint, this); Bind(wxEVT_LEFT_DOWN, &CMemoryView::OnMouseDownL, this); @@ -56,6 +61,38 @@ CMemoryView::CMemoryView(DebugInterface* debuginterface, wxWindow* parent) Bind(wxEVT_MOUSEWHEEL, &CMemoryView::OnScrollWheel, this); Bind(wxEVT_MENU, &CMemoryView::OnPopupMenu, this); Bind(wxEVT_SIZE, &CMemoryView::OnResize, this); + + SetDataType(MemoryDataType::FloatingPoint); + + // Every pixel will be drawn over in the paint event so erasing will just cause flickering. + SetBackgroundStyle(wxBG_STYLE_PAINT); +#if defined(__WXMSW__) || defined(__WXGTK__) + SetDoubleBuffered(true); +#endif +} + +void CMemoryView::SetDataType(MemoryDataType data_type) +{ + if (m_data_type == data_type) + return; + + m_data_type = data_type; + switch (data_type) + { + case MemoryDataType::FloatingPoint: + case MemoryDataType::ASCII: + align = 4; + break; + default: + align = 16; + m_last_hex_type = data_type; + break; + } + Refresh(); + + wxCommandEvent ev(DOLPHIN_EVT_MEMORY_VIEW_DATA_TYPE_CHANGED, GetId()); + ev.SetInt(static_cast<int>(data_type)); + GetEventHandler()->ProcessEvent(ev); } int CMemoryView::YToAddress(int y) @@ -68,10 +105,10 @@ int CMemoryView::YToAddress(int y) void CMemoryView::OnMouseDownL(wxMouseEvent& event) { - int x = event.m_x; - int y = event.m_y; + int x = event.GetX(); + int y = event.GetY(); - if (x > 16) + if (x > m_left_col_width) { oldSelection = selection; selection = YToAddress(y); @@ -99,7 +136,7 @@ void CMemoryView::OnMouseMove(wxMouseEvent& event) { wxRect rc = GetClientRect(); - if (event.m_leftDown && event.m_x > 16) + if (event.m_leftDown && event.m_x > m_left_col_width) { if (event.m_y < 0) { @@ -120,7 +157,7 @@ void CMemoryView::OnMouseMove(wxMouseEvent& event) void CMemoryView::OnMouseUpL(wxMouseEvent& event) { - if (event.m_x > 16) + if (event.m_x > m_left_col_width) { curAddress = YToAddress(event.m_y); selecting = false; @@ -137,11 +174,11 @@ void CMemoryView::OnScrollWheel(wxMouseEvent& event) if (scroll_down) { - curAddress += num_lines * 4; + curAddress += num_lines * align; } else { - curAddress -= num_lines * 4; + curAddress -= num_lines * align; } Refresh(); @@ -150,25 +187,26 @@ void CMemoryView::OnScrollWheel(wxMouseEvent& event) void CMemoryView::OnPopupMenu(wxCommandEvent& event) { - CFrame* main_frame = static_cast<CFrame*>(GetGrandParent()->GetParent()); - CCodeWindow* code_window = main_frame->g_pCodeWindow; - CWatchWindow* watch_window = code_window->m_WatchWindow; - -#if wxUSE_CLIPBOARD - wxTheClipboard->Open(); -#endif + // FIXME: This is terrible. Generate events instead. + CFrame* cframe = wxGetApp().GetCFrame(); + CCodeWindow* code_window = cframe->g_pCodeWindow; + CWatchWindow* watch_window = code_window->GetPanel<CWatchWindow>(); switch (event.GetId()) { #if wxUSE_CLIPBOARD case IDM_COPYADDRESS: + { + wxClipboardLocker clipboard_lock; wxTheClipboard->SetData(new wxTextDataObject(wxString::Format("%08x", selection))); - break; + } + break; case IDM_COPYHEX: { - std::string temp = StringFromFormat("%08x", debugger->ReadExtraMemory(memory, selection)); - wxTheClipboard->SetData(new wxTextDataObject(StrToWxStr(temp))); + wxClipboardLocker clipboard_lock; + wxTheClipboard->SetData(new wxTextDataObject( + wxString::Format("%08x", debugger->ReadExtraMemory(memory, selection)))); } break; #endif @@ -186,24 +224,21 @@ void CMemoryView::OnPopupMenu(wxCommandEvent& event) break; case IDM_VIEWASFP: - viewAsType = VIEWAS_FP; - Refresh(); + SetDataType(MemoryDataType::FloatingPoint); break; case IDM_VIEWASASCII: - viewAsType = VIEWAS_ASCII; - Refresh(); + SetDataType(MemoryDataType::ASCII); break; + case IDM_VIEWASHEX: - viewAsType = VIEWAS_HEX; - Refresh(); + SetDataType(m_last_hex_type); + break; + + default: + event.Skip(); break; } - -#if wxUSE_CLIPBOARD - wxTheClipboard->Close(); -#endif - event.Skip(); } void CMemoryView::OnMouseDownR(wxMouseEvent& event) @@ -216,12 +251,14 @@ void CMemoryView::OnMouseDownR(wxMouseEvent& event) menu.Append(IDM_COPYHEX, _("Copy &hex")); #endif menu.Append(IDM_WATCHADDRESS, _("Add to &watch")); - menu.Append(IDM_TOGGLEMEMORY, _("Toggle &memory")); + menu.AppendCheckItem(IDM_TOGGLEMEMORY, _("Toggle &memory"))->Check(memory != 0); wxMenu* viewAsSubMenu = new wxMenu; - viewAsSubMenu->Append(IDM_VIEWASFP, _("FP value")); - viewAsSubMenu->Append(IDM_VIEWASASCII, "ASCII"); - viewAsSubMenu->Append(IDM_VIEWASHEX, _("Hex")); + viewAsSubMenu->AppendRadioItem(IDM_VIEWASFP, _("FP value")) + ->Check(m_data_type == MemoryDataType::FloatingPoint); + viewAsSubMenu->AppendRadioItem(IDM_VIEWASASCII, "ASCII") + ->Check(m_data_type == MemoryDataType::ASCII); + viewAsSubMenu->AppendRadioItem(IDM_VIEWASHEX, _("Hex"))->Check(IsHexMode()); menu.AppendSubMenu(viewAsSubMenu, _("View As:")); PopupMenu(&menu); @@ -231,176 +268,160 @@ void CMemoryView::OnPaint(wxPaintEvent& event) { wxPaintDC dc(this); wxRect rc = GetClientRect(); - wxFont hFont("Courier"); - hFont.SetFamily(wxFONTFAMILY_TELETYPE); - wxCoord w, h; - dc.GetTextExtent("0WJyq", &w, &h, nullptr, nullptr, &hFont); - if (h > rowHeight) - rowHeight = h; - dc.GetTextExtent("0WJyq", &w, &h, nullptr, nullptr, &DebuggerFont); - if (h > rowHeight) - rowHeight = h; - - if (viewAsType == VIEWAS_HEX) - dc.SetFont(hFont); - else + if (DebuggerFont.IsFixedWidth()) + { dc.SetFont(DebuggerFont); + } + else + { + dc.SetFont(wxFont(DebuggerFont.GetPointSize(), wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, + wxFONTWEIGHT_NORMAL, false, "Courier")); + } - dc.GetTextExtent("W", &w, &h); - int fontSize = w; - int textPlacement = 17 + 9 * fontSize; + int font_width; + { + wxFontMetrics metrics = dc.GetFontMetrics(); + font_width = metrics.averageWidth; + if (metrics.height > rowHeight) + rowHeight = metrics.height; + } - // TODO: Add any drawing code here... - int width = rc.width; - int numRows = (rc.height / rowHeight) / 2 + 2; - dc.SetBackgroundMode(wxPENSTYLE_TRANSPARENT); - const wxColour bgColor = *wxWHITE; - wxPen nullPen(bgColor); - wxPen currentPen(*wxBLACK_PEN); - wxPen selPen(*wxGREY_PEN); - nullPen.SetStyle(wxPENSTYLE_TRANSPARENT); + const int row_start_x = m_left_col_width + 1; + const int mchk_x = FromDIP(LEFT_COL_WIDTH / 8); + const wxSize mchk_size = FromDIP(wxSize(LEFT_COL_WIDTH * 3 / 4, LEFT_COL_WIDTH * 3 / 4)); + const int mchk_offset_y = (rowHeight - mchk_size.GetHeight()) / 2; - wxBrush currentBrush(*wxLIGHT_GREY_BRUSH); - wxBrush pcBrush(*wxGREEN_BRUSH); - wxBrush mcBrush(*wxBLUE_BRUSH); - wxBrush bgBrush(bgColor); - wxBrush nullBrush(bgColor); - nullBrush.SetStyle(wxBRUSHSTYLE_TRANSPARENT); + int col_width = rc.width - m_left_col_width; + int num_rows = (rc.height / rowHeight) / 2 + 2; + const wxColour navy_color = wxTheColourDatabase->Find("NAVY"); - dc.SetPen(nullPen); - dc.SetBrush(bgBrush); - dc.DrawRectangle(0, 0, 16, rc.height); - dc.DrawRectangle(0, 0, rc.width, 5 + 8); + const int pen_width = FromDIP(1); + wxPen focus_pen(*wxBLACK, pen_width); + wxPen selection_pen(*wxLIGHT_GREY, pen_width); + wxBrush pc_brush(*wxGREEN_BRUSH); + wxBrush mc_brush(*wxBLUE_BRUSH); + wxBrush bg_brush(*wxWHITE_BRUSH); // TODO - clean up this freaking mess!!!!! - for (int row = -numRows; row <= numRows; row++) + for (int row = -num_rows; row <= num_rows; ++row) { - unsigned int address = curAddress + row * align; + u32 address = curAddress + row * align; - int rowY1 = rc.height / 2 + rowHeight * row - rowHeight / 2; - int rowY2 = rc.height / 2 + rowHeight * row + rowHeight / 2; + int row_y = rc.height / 2 + rowHeight * row - rowHeight / 2; + int row_x = row_start_x; + + auto draw_text = [&](const wxString& s, int offset_chars = 0, int min_length = 0) -> void { + dc.DrawText(s, row_x + font_width * offset_chars, row_y); + row_x += font_width * (std::max(static_cast<int>(s.size()), min_length) + offset_chars); + }; wxString temp = wxString::Format("%08x", address); u32 col = debugger->GetColor(address); wxBrush rowBrush(wxColour(col >> 16, col >> 8, col)); - dc.SetBrush(nullBrush); - dc.SetPen(nullPen); - dc.DrawRectangle(0, rowY1, 16, rowY2); + dc.SetBrush(bg_brush); + dc.SetPen(*wxTRANSPARENT_PEN); + dc.DrawRectangle(0, row_y, m_left_col_width, rowHeight); if (selecting && (address == selection)) - dc.SetPen(selPen); + dc.SetPen(selection_pen); else - dc.SetPen(row == 0 ? currentPen : nullPen); + dc.SetPen(row == 0 ? focus_pen : *wxTRANSPARENT_PEN); if (address == debugger->GetPC()) - dc.SetBrush(pcBrush); + dc.SetBrush(pc_brush); else dc.SetBrush(rowBrush); - dc.DrawRectangle(16, rowY1, width, rowY2 - 1); - dc.SetBrush(currentBrush); - dc.SetTextForeground("#600000"); // Dark red - dc.DrawText(temp, 17, rowY1); + dc.DrawRectangle(m_left_col_width, row_y, col_width, rowHeight); + dc.SetTextForeground(wxColour(0x60, 0x00, 0x00)); // Dark red + draw_text(temp); - if (viewAsType != VIEWAS_HEX) + if (!IsHexMode()) { char mem[256]; debugger->GetRawMemoryString(memory, address, mem, 256); - dc.SetTextForeground(wxTheColourDatabase->Find("NAVY")); - dc.DrawText(StrToWxStr(mem), 17 + fontSize * (8), rowY1); - dc.SetTextForeground(*wxBLACK); + dc.SetTextForeground(navy_color); + draw_text(StrToWxStr(mem), 2); + } + dc.SetTextForeground(*wxBLACK); + + // NOTE: We can trigger a segfault inside HostIsRAMAddress (nullptr) during shutdown + // because we still get paint events even though the core is being deleted so we + // need to make sure the Memory still exists. + // FIXME: This isn't relevant to the DSP Memory View + if (!debugger->IsAlive() || !Memory::IsInitialized() || !PowerPC::HostIsRAMAddress(address)) + continue; + + std::string dis; + // FIXME: This doesn't work with the DSP Debugger + u32 mem_data = debugger->ReadExtraMemory(memory, address); + + if (m_data_type == MemoryDataType::FloatingPoint) + { + float& flt = reinterpret_cast<float&>(mem_data); + dis = StringFromFormat("f: %f", flt); + } + else if (m_data_type == MemoryDataType::ASCII) + { + dis.reserve(4); + for (unsigned int i = 0; i < 4; ++i) + { + u8 byte = static_cast<u8>(mem_data >> (24 - i * 8)); + if (std::isprint(byte)) + dis += static_cast<char>(byte); + else + dis += ' '; + } + + Symbol* sym = g_symbolDB.GetSymbolFromAddr(mem_data); + if (sym) + { + dis += StringFromFormat(" # -> %s", sym->name.c_str()); + } + } + else + { + dis.reserve(48); + for (unsigned int i = 0; i < align; i += sizeof(u32)) + { + if (!PowerPC::HostIsRAMAddress(address + i)) + break; + u32 word = debugger->ReadExtraMemory(memory, address + i); + switch (m_data_type) + { + case MemoryDataType::U8: + default: + dis += StringFromFormat(" %02X %02X %02X %02X", (word >> 24) & 0xFF, (word >> 16) & 0xFF, + (word >> 8) & 0xFF, word & 0xFF); + break; + case MemoryDataType::U16: + dis += StringFromFormat(" %04X %04X", (word >> 16) & 0xFFFF, word & 0xFFFF); + break; + case MemoryDataType::U32: + dis += StringFromFormat(" %08X", word); + break; + } + } } - if (debugger->IsAlive()) + // Pad to a minimum of 48 characters for full fixed point float width + draw_text(StrToWxStr(dis), 2, 48); + + dc.SetTextForeground(*wxBLUE); + + std::string desc = debugger->GetDescription(address); + if (!desc.empty()) + draw_text(StrToWxStr(desc), 2); + + // Show blue memory check dot + if (debugger->IsMemCheck(address)) { - if (!PowerPC::HostIsRAMAddress(address)) - continue; - - std::string dis; - u32 mem_data = debugger->ReadExtraMemory(memory, address); - - if (viewAsType == VIEWAS_FP) - { - float flt = *(float*)(&mem_data); - dis = StringFromFormat("f: %f", flt); - } - else if (viewAsType == VIEWAS_ASCII) - { - u32 a[4] = {(mem_data & 0xff000000) >> 24, (mem_data & 0xff0000) >> 16, - (mem_data & 0xff00) >> 8, (mem_data & 0xff)}; - - for (auto& word : a) - { - if (word == '\0') - word = ' '; - } - - Symbol* sym = g_symbolDB.GetSymbolFromAddr(mem_data); - if (sym == nullptr) - dis = StringFromFormat("%c%c%c%c", a[0], a[1], a[2], a[3]); - else - dis = StringFromFormat("# -> %s", sym->name.c_str()); - } - else if (viewAsType == VIEWAS_HEX) - { - u32 mema[8] = {debugger->ReadExtraMemory(memory, address), - debugger->ReadExtraMemory(memory, address + 4), - debugger->ReadExtraMemory(memory, address + 8), - debugger->ReadExtraMemory(memory, address + 12), - debugger->ReadExtraMemory(memory, address + 16), - debugger->ReadExtraMemory(memory, address + 20), - debugger->ReadExtraMemory(memory, address + 24), - debugger->ReadExtraMemory(memory, address + 28)}; - - for (auto& word : mema) - { - switch (dataType) - { - case MemoryDataType::U8: - dis += StringFromFormat(" %02X %02X %02X %02X", ((word & 0xff000000) >> 24) & 0xFF, - ((word & 0xff0000) >> 16) & 0xFF, ((word & 0xff00) >> 8) & 0xFF, - word & 0xff); - break; - case MemoryDataType::U16: - dis += StringFromFormat(" %02X%02X %02X%02X", ((word & 0xff000000) >> 24) & 0xFF, - ((word & 0xff0000) >> 16) & 0xFF, ((word & 0xff00) >> 8) & 0xFF, - word & 0xff); - break; - case MemoryDataType::U32: - dis += StringFromFormat(" %02X%02X%02X%02X", ((word & 0xff000000) >> 24) & 0xFF, - ((word & 0xff0000) >> 16) & 0xFF, ((word & 0xff00) >> 8) & 0xFF, - word & 0xff); - break; - } - } - } - else - { - dis = "INVALID VIEWAS TYPE"; - } - - if (viewAsType != VIEWAS_HEX) - dc.DrawText(StrToWxStr(dis), textPlacement + fontSize * (8 + 8), rowY1); - else - dc.DrawText(StrToWxStr(dis), textPlacement, rowY1); - - dc.SetTextForeground(*wxBLUE); - - std::string desc = debugger->GetDescription(address); - if (!desc.empty()) - dc.DrawText(StrToWxStr(desc), 17 + fontSize * ((8 + 8 + 8 + 30) * 2), rowY1); - - // Show blue memory check dot - if (debugger->IsMemCheck(address)) - { - dc.SetBrush(mcBrush); - dc.DrawRectangle(8, rowY1 + 1, 11, 11); - } + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(mc_brush); + dc.DrawEllipse(mchk_x, row_y + mchk_offset_y, mchk_size.GetWidth(), mchk_size.GetHeight()); } } - - dc.SetPen(currentPen); } void CMemoryView::OnResize(wxSizeEvent& event) diff --git a/Source/Core/DolphinWX/Debugger/MemoryView.h b/Source/Core/DolphinWX/Debugger/MemoryView.h index 03cb68d21f..396b84b1c0 100644 --- a/Source/Core/DolphinWX/Debugger/MemoryView.h +++ b/Source/Core/DolphinWX/Debugger/MemoryView.h @@ -13,9 +13,13 @@ enum class MemoryDataType { U8, U16, - U32 + U32, + ASCII, + FloatingPoint }; +wxDECLARE_EVENT(DOLPHIN_EVT_MEMORY_VIEW_DATA_TYPE_CHANGED, wxCommandEvent); + class CMemoryView : public wxControl { public: @@ -29,12 +33,8 @@ public: Refresh(); } - void SetDataType(MemoryDataType data_type) - { - dataType = data_type; - Refresh(); - } - + void SetDataType(MemoryDataType data_type); + MemoryDataType GetDataType() const { return m_data_type; } void SetMemCheckOptions(bool read, bool write, bool log) { memCheckRead = read; @@ -43,6 +43,12 @@ public: } private: + int YToAddress(int y); + bool IsHexMode() const + { + return m_data_type != MemoryDataType::ASCII && m_data_type != MemoryDataType::FloatingPoint; + } + void OnPaint(wxPaintEvent& event); void OnMouseDownL(wxMouseEvent& event); void OnMouseMove(wxMouseEvent& event); @@ -50,14 +56,15 @@ private: void OnMouseDownR(wxMouseEvent& event); void OnScrollWheel(wxMouseEvent& event); void OnPopupMenu(wxCommandEvent& event); - - int YToAddress(int y); void OnResize(wxSizeEvent& event); + static constexpr int LEFT_COL_WIDTH = 16; + DebugInterface* debugger; - int align; + unsigned int align; int rowHeight; + int m_left_col_width; u32 selection; u32 oldSelection; @@ -65,18 +72,11 @@ private: int memory; int curAddress; - MemoryDataType dataType; bool memCheckRead; bool memCheckWrite; bool memCheckLog; - enum EViewAsType - { - VIEWAS_ASCII = 0, - VIEWAS_FP, - VIEWAS_HEX, - }; - - EViewAsType viewAsType; + MemoryDataType m_data_type; + MemoryDataType m_last_hex_type = MemoryDataType::U8; }; diff --git a/Source/Core/DolphinWX/Debugger/MemoryWindow.cpp b/Source/Core/DolphinWX/Debugger/MemoryWindow.cpp index 17dea4a17d..a70ba2db94 100644 --- a/Source/Core/DolphinWX/Debugger/MemoryWindow.cpp +++ b/Source/Core/DolphinWX/Debugger/MemoryWindow.cpp @@ -2,9 +2,10 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include <algorithm> +#include <array> #include <cstddef> #include <cstdio> -#include <cstring> #include <string> #include <vector> #include <wx/button.h> @@ -12,10 +13,13 @@ #include <wx/listbox.h> #include <wx/msgdlg.h> #include <wx/panel.h> +#include <wx/radiobox.h> #include <wx/radiobut.h> #include <wx/sizer.h> #include <wx/srchctrl.h> +#include <wx/stattext.h> #include <wx/textctrl.h> +#include <wx/utils.h> #include "Common/CommonTypes.h" #include "Common/FileUtil.h" @@ -43,9 +47,7 @@ enum IDM_DUMP_MEM2, IDM_DUMP_FAKEVMEM, IDM_VALBOX, - IDM_U8, - IDM_U16, - IDM_U32, + IDM_DATA_TYPE_RBOX, IDM_SEARCH, IDM_ASCII, IDM_HEX, @@ -53,30 +55,24 @@ enum }; BEGIN_EVENT_TABLE(CMemoryWindow, wxPanel) -EVT_LISTBOX(IDM_SYMBOLLIST, CMemoryWindow::OnSymbolListChange) -EVT_HOST_COMMAND(wxID_ANY, CMemoryWindow::OnHostMessage) EVT_BUTTON(IDM_SETVALBUTTON, CMemoryWindow::SetMemoryValue) EVT_BUTTON(IDM_DUMP_MEMORY, CMemoryWindow::OnDumpMemory) EVT_BUTTON(IDM_DUMP_MEM2, CMemoryWindow::OnDumpMem2) EVT_BUTTON(IDM_DUMP_FAKEVMEM, CMemoryWindow::OnDumpFakeVMEM) -EVT_CHECKBOX(IDM_U8, CMemoryWindow::U8) -EVT_CHECKBOX(IDM_U16, CMemoryWindow::U16) -EVT_CHECKBOX(IDM_U32, CMemoryWindow::U32) -EVT_BUTTON(IDM_SEARCH, CMemoryWindow::onSearch) -EVT_CHECKBOX(IDM_ASCII, CMemoryWindow::onAscii) -EVT_CHECKBOX(IDM_HEX, CMemoryWindow::onHex) -EVT_RADIOBUTTON(IDM_MEMCHECK_OPTIONS_CHANGE, CMemoryWindow::onMemCheckOptionChange) -EVT_CHECKBOX(IDM_MEMCHECK_OPTIONS_CHANGE, CMemoryWindow::onMemCheckOptionChange) +EVT_RADIOBOX(IDM_DATA_TYPE_RBOX, CMemoryWindow::OnDataTypeChanged) +EVT_BUTTON(IDM_SEARCH, CMemoryWindow::OnSearch) +EVT_RADIOBUTTON(IDM_MEMCHECK_OPTIONS_CHANGE, CMemoryWindow::OnMemCheckOptionChange) +EVT_CHECKBOX(IDM_MEMCHECK_OPTIONS_CHANGE, CMemoryWindow::OnMemCheckOptionChange) END_EVENT_TABLE() -CMemoryWindow::CMemoryWindow(CCodeWindow* code_window, wxWindow* parent, wxWindowID id, - const wxPoint& pos, const wxSize& size, long style, - const wxString& name) - : wxPanel(parent, id, pos, size, style, name), m_code_window(code_window) +CMemoryWindow::CMemoryWindow(wxWindow* parent, wxWindowID id, const wxPoint& pos, + const wxSize& size, long style, const wxString& name) + : wxPanel(parent, id, pos, size, style, name) { DebugInterface* di = &PowerPC::debug_interface; memview = new CMemoryView(di, this); + memview->Bind(DOLPHIN_EVT_MEMORY_VIEW_DATA_TYPE_CHANGED, &CMemoryWindow::OnDataTypeChanged, this); addrbox = new wxSearchCtrl(this, IDM_MEM_ADDRBOX); addrbox->Bind(wxEVT_TEXT, &CMemoryWindow::OnAddrBoxChange, this); @@ -85,13 +81,17 @@ CMemoryWindow::CMemoryWindow(CCodeWindow* code_window, wxWindow* parent, wxWindo valbox = new wxTextCtrl(this, IDM_VALBOX, "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); valbox->Bind(wxEVT_TEXT_ENTER, &CMemoryWindow::SetMemoryValueFromValBox, this); + valbox->Bind(wxEVT_TEXT, &CMemoryWindow::OnValueChanged, this); - wxGridSizer* const search_sizer = new wxGridSizer(1); - search_sizer->Add(addrbox); + const int space3 = FromDIP(3); + const int space5 = FromDIP(5); + + wxBoxSizer* const search_sizer = new wxBoxSizer(wxVERTICAL); + search_sizer->Add(addrbox, 0, wxEXPAND); search_sizer->Add(valbox, 0, wxEXPAND); search_sizer->Add(new wxButton(this, IDM_SETVALBUTTON, _("Set Value"))); - wxGridSizer* const dump_sizer = new wxGridSizer(1); + wxBoxSizer* const dump_sizer = new wxBoxSizer(wxVERTICAL); dump_sizer->Add(new wxButton(this, IDM_DUMP_MEMORY, _("Dump MRAM")), 0, wxEXPAND); dump_sizer->Add(new wxButton(this, IDM_DUMP_MEM2, _("Dump EXRAM")), 0, wxEXPAND); if (!SConfig::GetInstance().bMMU) @@ -99,73 +99,57 @@ CMemoryWindow::CMemoryWindow(CCodeWindow* code_window, wxWindow* parent, wxWindo wxStaticBoxSizer* const sizerSearchType = new wxStaticBoxSizer(wxVERTICAL, this, _("Search")); sizerSearchType->Add(btnSearch = new wxButton(this, IDM_SEARCH, _("Search"))); - sizerSearchType->Add(chkAscii = new wxCheckBox(this, IDM_ASCII, "Ascii ")); - sizerSearchType->Add(chkHex = new wxCheckBox(this, IDM_HEX, _("Hex"))); + sizerSearchType->Add(m_rb_ascii = new wxRadioButton(this, IDM_ASCII, "Ascii", wxDefaultPosition, + wxDefaultSize, wxRB_GROUP)); + sizerSearchType->Add(m_rb_hex = new wxRadioButton(this, IDM_HEX, _("Hex"))); + m_search_result_msg = + new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, + wxST_NO_AUTORESIZE | wxALIGN_CENTER_HORIZONTAL); + sizerSearchType->Add(m_search_result_msg, 0, wxEXPAND); - wxStaticBoxSizer* const sizerDataTypes = new wxStaticBoxSizer(wxVERTICAL, this, _("Data Type")); - sizerDataTypes->SetMinSize(74, 40); - sizerDataTypes->Add(chk8 = new wxCheckBox(this, IDM_U8, "U8")); - sizerDataTypes->Add(chk16 = new wxCheckBox(this, IDM_U16, "U16")); - sizerDataTypes->Add(chk32 = new wxCheckBox(this, IDM_U32, "U32")); + wxArrayString data_type_options; + data_type_options.Add("U8"); + data_type_options.Add("U16"); + data_type_options.Add("U32"); + data_type_options.Add("ASCII"); + data_type_options.Add("Float32"); + m_rbox_data_type = new wxRadioBox(this, IDM_DATA_TYPE_RBOX, _("Data Type"), wxDefaultPosition, + wxDefaultSize, data_type_options, 1); - wxStaticBoxSizer* const sizerMemCheckOptions = + wxStaticBoxSizer* const memcheck_options_sizer = new wxStaticBoxSizer(wxVERTICAL, this, "Memory check options"); - sizerMemCheckOptions->Add(rdbReadWrite = new wxRadioButton(this, IDM_MEMCHECK_OPTIONS_CHANGE, - "Read and Write", wxDefaultPosition, - wxDefaultSize, wxRB_GROUP)); - sizerMemCheckOptions->Add(rdbRead = - new wxRadioButton(this, IDM_MEMCHECK_OPTIONS_CHANGE, "Read only")); - sizerMemCheckOptions->Add(rdbWrite = - new wxRadioButton(this, IDM_MEMCHECK_OPTIONS_CHANGE, "Write only")); - sizerMemCheckOptions->Add(chkLog = new wxCheckBox(this, IDM_MEMCHECK_OPTIONS_CHANGE, "Log")); + memcheck_options_sizer->Add(rdbReadWrite = new wxRadioButton(this, IDM_MEMCHECK_OPTIONS_CHANGE, + "Read and Write", wxDefaultPosition, + wxDefaultSize, wxRB_GROUP)); + memcheck_options_sizer->Add( + rdbRead = new wxRadioButton(this, IDM_MEMCHECK_OPTIONS_CHANGE, "Read only")); + memcheck_options_sizer->Add( + rdbWrite = new wxRadioButton(this, IDM_MEMCHECK_OPTIONS_CHANGE, "Write only")); + memcheck_options_sizer->Add(chkLog = new wxCheckBox(this, IDM_MEMCHECK_OPTIONS_CHANGE, "Log")); wxBoxSizer* const sizerRight = new wxBoxSizer(wxVERTICAL); sizerRight->Add(search_sizer); - sizerRight->AddSpacer(5); - sizerRight->Add(dump_sizer); - sizerRight->Add(sizerSearchType); - sizerRight->Add(sizerDataTypes); - sizerRight->Add(sizerMemCheckOptions); + sizerRight->AddSpacer(space5); + sizerRight->Add(dump_sizer, 0, wxEXPAND); + sizerRight->Add(sizerSearchType, 0, wxEXPAND); + sizerRight->Add(m_rbox_data_type, 0, wxEXPAND); + sizerRight->Add(memcheck_options_sizer, 0, wxEXPAND); wxBoxSizer* const sizerBig = new wxBoxSizer(wxHORIZONTAL); sizerBig->Add(memview, 20, wxEXPAND); - sizerBig->Add(sizerRight, 0, wxEXPAND | wxALL, 3); + sizerBig->AddSpacer(space3); + sizerBig->Add(sizerRight, 0, wxEXPAND | wxTOP | wxBOTTOM, space3); + sizerBig->AddSpacer(space3); SetSizer(sizerBig); - chkHex->SetValue(1); // Set defaults - chk8->SetValue(1); - chkLog->SetValue(1); + m_rb_hex->SetValue(true); // Set defaults + chkLog->SetValue(true); + m_rbox_data_type->SetSelection(static_cast<int>(memview->GetDataType())); sizerRight->Fit(this); sizerBig->Fit(this); } -void CMemoryWindow::Save(IniFile& ini) const -{ - // Prevent these bad values that can happen after a crash or hanging - if (GetPosition().x != -32000 && GetPosition().y != -32000) - { - IniFile::Section* mem_window = ini.GetOrCreateSection("MemoryWindow"); - mem_window->Set("x", GetPosition().x); - mem_window->Set("y", GetPosition().y); - mem_window->Set("w", GetSize().GetWidth()); - mem_window->Set("h", GetSize().GetHeight()); - } -} - -void CMemoryWindow::Load(IniFile& ini) -{ - int x, y, w, h; - - IniFile::Section* mem_window = ini.GetOrCreateSection("MemoryWindow"); - mem_window->Get("x", &x, GetPosition().x); - mem_window->Get("y", &y, GetPosition().y); - mem_window->Get("w", &w, GetSize().GetWidth()); - mem_window->Get("h", &h, GetSize().GetHeight()); - - SetSize(x, y, w, h); -} - void CMemoryWindow::JumpToAddress(u32 _Address) { memview->Center(_Address); @@ -219,55 +203,14 @@ void CMemoryWindow::OnAddrBoxChange(wxCommandEvent& event) event.Skip(); } -void CMemoryWindow::Update() +void CMemoryWindow::Repopulate() { - memview->Refresh(); memview->Center(PC); } -void CMemoryWindow::NotifyMapLoaded() +void CMemoryWindow::OnValueChanged(wxCommandEvent&) { - symbols->Show(false); // hide it for faster filling - symbols->Clear(); -#if 0 -#ifdef _WIN32 - const FunctionDB::XFuncMap &syms = g_symbolDB.Symbols(); - for (FuntionDB::XFuncMap::iterator iter = syms.begin(); iter != syms.end(); ++iter) - { - int idx = symbols->Append(iter->second.name.c_str()); - symbols->SetClientData(idx, (void*)&iter->second); - } -#endif -#endif - symbols->Show(true); - Update(); -} - -void CMemoryWindow::OnSymbolListChange(wxCommandEvent& event) -{ - int index = symbols->GetSelection(); - if (index >= 0) - { - Symbol* pSymbol = static_cast<Symbol*>(symbols->GetClientData(index)); - if (pSymbol != nullptr) - { - memview->Center(pSymbol->address); - } - } -} - -void CMemoryWindow::OnHostMessage(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case IDM_NOTIFY_MAP_LOADED: - NotifyMapLoaded(); - break; - case IDM_UPDATE_BREAKPOINTS: - if (m_code_window->m_BreakpointWindow) - m_code_window->m_BreakpointWindow->NotifyUpdate(); - break; - } + m_continue_search = false; } static void DumpArray(const std::string& filename, const u8* data, size_t length) @@ -304,39 +247,40 @@ void CMemoryWindow::OnDumpFakeVMEM(wxCommandEvent& event) DumpArray(File::GetUserPath(F_FAKEVMEMDUMP_IDX), Memory::m_pFakeVMEM, Memory::FAKEVMEM_SIZE); } -void CMemoryWindow::U8(wxCommandEvent& event) +void CMemoryWindow::OnDataTypeChanged(wxCommandEvent& ev) { - chk16->SetValue(0); - chk32->SetValue(0); - memview->SetDataType(MemoryDataType::U8); + static constexpr std::array<MemoryDataType, 5> map{{MemoryDataType::U8, MemoryDataType::U16, + MemoryDataType::U32, MemoryDataType::ASCII, + MemoryDataType::FloatingPoint}}; + if (ev.GetId() == IDM_DATA_TYPE_RBOX) + { + memview->SetDataType(map.at(ev.GetSelection())); + } + else + { + // Event from the CMemoryView indicating type was changed. + auto itr = std::find(map.begin(), map.end(), static_cast<MemoryDataType>(ev.GetInt())); + int idx = -1; + if (itr != map.end()) + idx = static_cast<int>(itr - map.begin()); + m_rbox_data_type->SetSelection(idx); + } } -void CMemoryWindow::U16(wxCommandEvent& event) +void CMemoryWindow::OnSearch(wxCommandEvent& event) { - chk8->SetValue(0); - chk32->SetValue(0); - memview->SetDataType(MemoryDataType::U16); -} - -void CMemoryWindow::U32(wxCommandEvent& event) -{ - chk16->SetValue(0); - chk8->SetValue(0); - memview->SetDataType(MemoryDataType::U32); -} - -void CMemoryWindow::onSearch(wxCommandEvent& event) -{ - u8* TheRAM = nullptr; - u32 szRAM = 0; + wxBusyCursor hourglass_cursor; + u8* ram_ptr = nullptr; + u32 ram_size = 0; + // NOTE: We're assuming the base address is zero. switch (memview->GetMemoryType()) { case 0: default: if (Memory::m_pRAM) { - TheRAM = Memory::m_pRAM; - szRAM = Memory::REALRAM_SIZE; + ram_ptr = Memory::m_pRAM; + ram_size = Memory::REALRAM_SIZE; } break; case 1: @@ -344,131 +288,115 @@ void CMemoryWindow::onSearch(wxCommandEvent& event) u8* aram = DSP::GetARAMPtr(); if (aram) { - TheRAM = aram; - szRAM = DSP::ARAM_SIZE; + ram_ptr = aram; + ram_size = DSP::ARAM_SIZE; } } break; } - // Now we have memory to look in - // Are we looking for ASCII string, or hex? - // memview->cu - wxString rawData = valbox->GetValue(); - std::vector<u8> Dest; // May need a better name - u32 size = 0; - int pad = rawData.size() % 2; // If it's uneven - unsigned int i = 0; - long count = 0; - char copy[3] = {0}; - long newsize = 0; - unsigned char* tmp2 = nullptr; - char* tmpstr = nullptr; - - if (chkHex->GetValue()) + if (!ram_ptr) { - // We are looking for hex - // If it's uneven - size = (rawData.size() / 2) + pad; - Dest.resize(size + 32); - newsize = rawData.size(); + m_search_result_msg->SetLabel(_("Memory Not Ready")); + return; + } - if (pad) + std::vector<u8> search_bytes; + wxString search_val = valbox->GetValue(); + + if (m_rb_hex->GetValue()) + { + search_val.Trim(true).Trim(false); + // If there's a trailing nybble, stick a zero in front to make it a byte + if (search_val.size() & 1) + search_val.insert(0, 1, '0'); + search_bytes.reserve(search_val.size() / 2); + + wxString conversion_buffer(2, ' '); + for (std::size_t i = 0; i < search_val.size(); i += 2) { - tmpstr = new char[newsize + 2]; - memset(tmpstr, 0, newsize + 2); - tmpstr[0] = '0'; + unsigned long byte = 0; + conversion_buffer[0] = search_val[i]; + conversion_buffer[1] = search_val[i + 1]; + if (!conversion_buffer.ToULong(&byte, 16)) + { + m_search_result_msg->SetLabel(_("Not Valid Hex")); + return; + } + search_bytes.push_back(static_cast<u8>(byte)); } - else - { - tmpstr = new char[newsize + 1]; - memset(tmpstr, 0, newsize + 1); - } - strcat(tmpstr, WxStrToStr(rawData).c_str()); - tmp2 = &Dest.front(); - count = 0; - for (i = 0; i < strlen(tmpstr); i++) - { - copy[0] = tmpstr[i]; - copy[1] = tmpstr[i + 1]; - copy[2] = 0; - int tmpint; - sscanf(copy, "%02x", &tmpint); - tmp2[count++] = tmpint; - // Dest[count] should now be the hex of what the two chars were! - // Also should add a check to make sure it's A-F only - // sscanf(copy, "%02x", &tmp2[count++]); - i += 1; - } - delete[] tmpstr; } else { - // Looking for an ascii string - size = rawData.size(); - Dest.resize(size + 1); - tmpstr = new char[size + 1]; - - tmp2 = &Dest.front(); - sprintf(tmpstr, "%s", WxStrToStr(rawData).c_str()); - - for (i = 0; i < size; i++) - tmp2[i] = tmpstr[i]; - - delete[] tmpstr; + const auto& bytes = search_val.ToUTF8(); + search_bytes.assign(bytes.data(), bytes.data() + bytes.length()); } + search_val.Clear(); - if (size) + // For completeness + if (search_bytes.size() > ram_size) { - unsigned char* pnt = &Dest.front(); - unsigned int k = 0; - // grab - wxString txt = addrbox->GetValue(); - u32 addr = 0; - if (txt.size()) + m_search_result_msg->SetLabel(_("Value Too Large")); + return; + } + + if (search_bytes.empty()) + { + m_search_result_msg->SetLabel(_("No Value Given")); + return; + } + + // Search starting from specified address if there is one. + u32 addr = 0; // Base address + { + wxString addr_val = addrbox->GetValue(); + addr_val.Trim(true).Trim(false); + if (!addr_val.empty()) { - sscanf(WxStrToStr(txt).c_str(), "%08x", &addr); - } - i = addr + 4; - for (; i < szRAM; ++i) - { - for (k = 0; k < size; ++k) + unsigned long addr_ul = 0; + if (addr_val.ToULong(&addr_ul, 16)) { - if (i + k > szRAM) - break; - if (k > size) - break; - if (pnt[k] != TheRAM[i + k]) - { - k = 0; - break; - } - } - if (k == size) - { - // Match was found - wxMessageBox(_("A match was found. Placing viewer at the offset.")); - addrbox->SetValue(wxString::Format("%08x", i)); - // memview->curAddress = i; - // memview->Refresh(); - OnAddrBoxChange(event); - return; + addr = static_cast<u32>(addr_ul); + // Don't find the result we're already looking at + if (m_continue_search && addr == m_last_search_address) + addr += 1; } } - wxMessageBox(_("No match was found.")); + } + + // If the current address doesn't leave enough bytes to search then we're done. + if (addr >= ram_size - search_bytes.size()) + { + m_search_result_msg->SetLabel(_("Address Out of Range")); + return; + } + + u8* end = &ram_ptr[ram_size - search_bytes.size() + 1]; + u8* ptr = &ram_ptr[addr]; + while (true) + { + ptr = std::find(ptr, end, search_bytes[0]); + if (ptr == end) + { + m_search_result_msg->SetLabel(_("No Match")); + break; + } + + if (std::equal(search_bytes.begin(), search_bytes.end(), ptr)) + { + m_search_result_msg->SetLabel(_("Match Found")); + u32 offset = static_cast<u32>(ptr - ram_ptr); + // NOTE: SetValue() generates a synthetic wxEVT_TEXT + addrbox->SetValue(wxString::Format("%08x", offset)); + m_last_search_address = offset; + m_continue_search = true; + break; + } + + ++ptr; } } -void CMemoryWindow::onAscii(wxCommandEvent& event) -{ - chkHex->SetValue(0); -} - -void CMemoryWindow::onHex(wxCommandEvent& event) -{ - chkAscii->SetValue(0); -} - -void CMemoryWindow::onMemCheckOptionChange(wxCommandEvent& event) +void CMemoryWindow::OnMemCheckOptionChange(wxCommandEvent& event) { if (rdbReadWrite->GetValue()) memview->SetMemCheckOptions(true, true, chkLog->GetValue()); diff --git a/Source/Core/DolphinWX/Debugger/MemoryWindow.h b/Source/Core/DolphinWX/Debugger/MemoryWindow.h index f9ff2ca285..cf3800e292 100644 --- a/Source/Core/DolphinWX/Debugger/MemoryWindow.h +++ b/Source/Core/DolphinWX/Debugger/MemoryWindow.h @@ -12,51 +12,46 @@ class CCodeWindow; class IniFile; class wxButton; class wxCheckBox; +class wxRadioBox; +class wxRadioButton; class wxListBox; class wxSearchCtrl; +class wxStaticText; class wxTextCtrl; class wxRadioButton; class CMemoryWindow : public wxPanel { public: - CMemoryWindow(CCodeWindow* code_window, wxWindow* parent, wxWindowID id = wxID_ANY, - const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, - long style = wxTAB_TRAVERSAL | wxBORDER_NONE, const wxString& name = _("Memory")); + CMemoryWindow(wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL | wxBORDER_NONE, + const wxString& name = _("Memory")); - void Save(IniFile& _IniFile) const; - void Load(IniFile& _IniFile); - - void Update() override; - void NotifyMapLoaded(); + void Repopulate(); void JumpToAddress(u32 _Address); private: DECLARE_EVENT_TABLE() - void U8(wxCommandEvent& event); - void U16(wxCommandEvent& event); - void U32(wxCommandEvent& event); - void onSearch(wxCommandEvent& event); - void onAscii(wxCommandEvent& event); - void onHex(wxCommandEvent& event); - void OnSymbolListChange(wxCommandEvent& event); + void OnDataTypeChanged(wxCommandEvent& event); + void OnSearch(wxCommandEvent& event); void OnAddrBoxChange(wxCommandEvent& event); - void OnHostMessage(wxCommandEvent& event); + void OnValueChanged(wxCommandEvent&); void SetMemoryValueFromValBox(wxCommandEvent& event); void SetMemoryValue(wxCommandEvent& event); void OnDumpMemory(wxCommandEvent& event); void OnDumpMem2(wxCommandEvent& event); void OnDumpFakeVMEM(wxCommandEvent& event); - void onMemCheckOptionChange(wxCommandEvent& event); + void OnMemCheckOptionChange(wxCommandEvent& event); - wxCheckBox* chk8; - wxCheckBox* chk16; - wxCheckBox* chk32; wxButton* btnSearch; - wxCheckBox* chkAscii; - wxCheckBox* chkHex; + wxRadioButton* m_rb_ascii; + wxRadioButton* m_rb_hex; + + wxRadioBox* m_rbox_data_type; + wxStaticText* m_search_result_msg; + wxCheckBox* chkLog; wxRadioButton* rdbRead; wxRadioButton* rdbWrite; @@ -65,8 +60,10 @@ private: CCodeWindow* m_code_window; CMemoryView* memview; - wxListBox* symbols; wxSearchCtrl* addrbox; wxTextCtrl* valbox; + + u32 m_last_search_address = 0; + bool m_continue_search = false; }; diff --git a/Source/Core/DolphinWX/Debugger/RegisterView.cpp b/Source/Core/DolphinWX/Debugger/RegisterView.cpp index 96518b3ea3..002e29e9e1 100644 --- a/Source/Core/DolphinWX/Debugger/RegisterView.cpp +++ b/Source/Core/DolphinWX/Debugger/RegisterView.cpp @@ -19,6 +19,7 @@ #include "DolphinWX/Debugger/WatchWindow.h" #include "DolphinWX/Frame.h" #include "DolphinWX/Globals.h" +#include "DolphinWX/Main.h" #include "DolphinWX/WxUtils.h" // F-zero 80005e60 wtf?? @@ -466,7 +467,7 @@ CRegisterView::CRegisterView(wxWindow* parent, wxWindowID id) : wxGrid(parent, i AutoSizeColumns(); } -void CRegisterView::Update() +void CRegisterView::Repopulate() { m_register_table->UpdateCachedRegs(); ForceRefresh(); @@ -507,10 +508,11 @@ void CRegisterView::OnMouseDownR(wxGridEvent& event) void CRegisterView::OnPopupMenu(wxCommandEvent& event) { - CFrame* main_frame = static_cast<CFrame*>(GetGrandParent()->GetParent()); - CCodeWindow* code_window = main_frame->g_pCodeWindow; - CWatchWindow* watch_window = code_window->m_WatchWindow; - CMemoryWindow* memory_window = code_window->m_MemoryWindow; + // FIXME: This is terrible. Generate events instead. + CFrame* cframe = wxGetApp().GetCFrame(); + CCodeWindow* code_window = cframe->g_pCodeWindow; + CWatchWindow* watch_window = code_window->GetPanel<CWatchWindow>(); + CMemoryWindow* memory_window = code_window->GetPanel<CMemoryWindow>(); switch (event.GetId()) { diff --git a/Source/Core/DolphinWX/Debugger/RegisterView.h b/Source/Core/DolphinWX/Debugger/RegisterView.h index 5da6c206fe..d17696f67c 100644 --- a/Source/Core/DolphinWX/Debugger/RegisterView.h +++ b/Source/Core/DolphinWX/Debugger/RegisterView.h @@ -51,7 +51,7 @@ public: void UpdateCachedRegs(); private: - static constexpr size_t NUM_SPECIALS = 14; + static constexpr int NUM_SPECIALS = 14; std::array<u32, 32> m_CachedRegs{}; std::array<u32, NUM_SPECIALS> m_CachedSpecialRegs{}; @@ -72,7 +72,7 @@ class CRegisterView : public wxGrid { public: CRegisterView(wxWindow* parent, wxWindowID id = wxID_ANY); - void Update() override; + void Repopulate(); private: void OnMouseDownR(wxGridEvent& event); diff --git a/Source/Core/DolphinWX/Debugger/RegisterWindow.cpp b/Source/Core/DolphinWX/Debugger/RegisterWindow.cpp index 03d1afbab8..b849eb86fe 100644 --- a/Source/Core/DolphinWX/Debugger/RegisterWindow.cpp +++ b/Source/Core/DolphinWX/Debugger/RegisterWindow.cpp @@ -21,7 +21,7 @@ void CRegisterWindow::CreateGUIControls() { wxBoxSizer* sGrid = new wxBoxSizer(wxVERTICAL); m_GPRGridView = new CRegisterView(this); - sGrid->Add(m_GPRGridView, 1, wxGROW); + sGrid->Add(m_GPRGridView, 1, wxEXPAND); SetSizer(sGrid); NotifyUpdate(); @@ -30,5 +30,5 @@ void CRegisterWindow::CreateGUIControls() void CRegisterWindow::NotifyUpdate() { if (m_GPRGridView != nullptr) - m_GPRGridView->Update(); + m_GPRGridView->Repopulate(); } diff --git a/Source/Core/DolphinWX/Debugger/WatchView.cpp b/Source/Core/DolphinWX/Debugger/WatchView.cpp index ec3227b33d..1eab82db35 100644 --- a/Source/Core/DolphinWX/Debugger/WatchView.cpp +++ b/Source/Core/DolphinWX/Debugger/WatchView.cpp @@ -18,6 +18,7 @@ #include "DolphinWX/Debugger/WatchView.h" #include "DolphinWX/Debugger/WatchWindow.h" #include "DolphinWX/Frame.h" +#include "DolphinWX/Main.h" #include "DolphinWX/WxUtils.h" enum @@ -232,7 +233,7 @@ CWatchView::CWatchView(wxWindow* parent, wxWindowID id) : wxGrid(parent, id) Bind(wxEVT_MENU, &CWatchView::OnPopupMenu, this); } -void CWatchView::Update() +void CWatchView::Repopulate() { if (Core::IsRunning()) { @@ -269,11 +270,12 @@ void CWatchView::OnMouseDownR(wxGridEvent& event) void CWatchView::OnPopupMenu(wxCommandEvent& event) { - CFrame* main_frame = static_cast<CFrame*>(GetGrandParent()->GetParent()); - CCodeWindow* code_window = main_frame->g_pCodeWindow; - CWatchWindow* watch_window = code_window->m_WatchWindow; - CMemoryWindow* memory_window = code_window->m_MemoryWindow; - CBreakPointWindow* breakpoint_window = code_window->m_BreakpointWindow; + // FIXME: This is terrible. Generate events instead. + CFrame* cframe = wxGetApp().GetCFrame(); + CCodeWindow* code_window = cframe->g_pCodeWindow; + CWatchWindow* watch_window = code_window->GetPanel<CWatchWindow>(); + CMemoryWindow* memory_window = code_window->GetPanel<CMemoryWindow>(); + CBreakPointWindow* breakpoint_window = code_window->GetPanel<CBreakPointWindow>(); wxString strNewVal; TMemCheck MemCheck; diff --git a/Source/Core/DolphinWX/Debugger/WatchView.h b/Source/Core/DolphinWX/Debugger/WatchView.h index b00c290a0b..624526fe19 100644 --- a/Source/Core/DolphinWX/Debugger/WatchView.h +++ b/Source/Core/DolphinWX/Debugger/WatchView.h @@ -37,7 +37,7 @@ class CWatchView : public wxGrid { public: CWatchView(wxWindow* parent, wxWindowID id = wxID_ANY); - void Update() override; + void Repopulate(); private: void OnMouseDownR(wxGridEvent& event); diff --git a/Source/Core/DolphinWX/Debugger/WatchWindow.cpp b/Source/Core/DolphinWX/Debugger/WatchWindow.cpp index f43190a5d3..45a55eebe5 100644 --- a/Source/Core/DolphinWX/Debugger/WatchWindow.cpp +++ b/Source/Core/DolphinWX/Debugger/WatchWindow.cpp @@ -25,9 +25,12 @@ public: : DolphinAuiToolBar(parent, id, wxDefaultPosition, wxDefaultSize, wxAUI_TB_DEFAULT_STYLE | wxAUI_TB_TEXT) { - SetToolBitmapSize(wxSize(16, 16)); + wxSize bitmap_size = FromDIP(wxSize(16, 16)); + SetToolBitmapSize(bitmap_size); - m_Bitmaps[Toolbar_File] = WxUtils::LoadResourceBitmap("toolbar_debugger_delete"); + m_Bitmaps[Toolbar_File] = WxUtils::LoadScaledResourceBitmap( + "toolbar_debugger_delete", this, bitmap_size, wxDefaultSize, + WxUtils::LSI_SCALE_DOWN | WxUtils::LSI_ALIGN_CENTER); AddTool(ID_LOAD, _("Load"), m_Bitmaps[Toolbar_File]); Bind(wxEVT_TOOL, &CWatchWindow::Event_LoadAll, parent, ID_LOAD); @@ -80,7 +83,7 @@ CWatchWindow::~CWatchWindow() void CWatchWindow::NotifyUpdate() { if (m_GPRGridView != nullptr) - m_GPRGridView->Update(); + m_GPRGridView->Repopulate(); } void CWatchWindow::Event_SaveAll(wxCommandEvent& WXUNUSED(event)) diff --git a/Source/Core/DolphinWX/DolphinSlider.cpp b/Source/Core/DolphinWX/DolphinSlider.cpp new file mode 100644 index 0000000000..673c5fd4cc --- /dev/null +++ b/Source/Core/DolphinWX/DolphinSlider.cpp @@ -0,0 +1,126 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include <wx/utils.h> + +#include "DolphinWX/DolphinSlider.h" + +#ifdef __WXMSW__ +#define WIN32_LEAN_AND_MEAN 1 +// clang-format off +#include <Windows.h> +#include <CommCtrl.h> +// clang-format on +#endif + +static constexpr int SLIDER_MIN_LENGTH = 100; + +DolphinSlider::DolphinSlider() = default; +DolphinSlider::~DolphinSlider() = default; + +bool DolphinSlider::Create(wxWindow* parent, wxWindowID id, int value, int min_val, int max_val, + const wxPoint& pos, const wxSize& size, long style, + const wxValidator& validator, const wxString& name) +{ + // Sanitize the style flags. + // We don't want any label flags because those break DPI scaling on wxMSW, + // wxWidgets will internally lock the height of the slider to 32 pixels. + style &= ~wxSL_LABELS; + + return wxSlider::Create(parent, id, value, min_val, max_val, pos, size, style, validator, name); +} + +wxSize DolphinSlider::DoGetBestClientSize() const +{ +#ifdef __WXMSW__ + int ticks = 0; + int default_length = FromDIP(SLIDER_MIN_LENGTH); + if (HasFlag(wxSL_TICKS)) + { + // NOTE: Ticks do not scale at all (on Win7) + default_length += 4; + ticks = 6; + } + + int metric = 0; + { + // We need to determine the maximum thumb size because unfortunately the thumb size + // is controlled by the theme so may have a varying maximum size limit. + // NOTE: We can't use ourself because we're const so we can't change our size. + // NOTE: This is less inefficient then it seems, DoGetBestSize() is only called once + // per instance and cached until InvalidateBestSize() is called. + wxSlider* helper = new wxSlider(GetParent(), wxID_ANY, GetValue(), GetMin(), GetMax(), + wxDefaultPosition, FromDIP(wxSize(100, 100)), GetWindowStyle()); + ::RECT r{}; + ::SendMessageW(reinterpret_cast<HWND>(helper->GetHWND()), TBM_GETTHUMBRECT, 0, + reinterpret_cast<LPARAM>(&r)); + helper->Destroy(); + + // Breakdown metrics + int computed_size; + int scroll_size; + if (HasFlag(wxSL_VERTICAL)) + { + // Trackbar thumb does not directly touch the edge, we add the padding + // a second time to pad the other edge to make it symmetric. + computed_size = static_cast<int>(r.right + r.left); + scroll_size = ::GetSystemMetrics(SM_CXVSCROLL); + } + else + { + computed_size = static_cast<int>(r.bottom + r.top); + scroll_size = ::GetSystemMetrics(SM_CYHSCROLL); + } + + // This is based on how Microsoft calculates trackbar sizes in the .Net Framework + // when using automatic sizing in WinForms. + int max = scroll_size * 2; + + metric = wxClip(computed_size, scroll_size, max); + } + + if (HasFlag(wxSL_VERTICAL)) + return wxSize(metric + ticks, default_length); + return wxSize(default_length, metric + ticks); +#else + wxSize base_size = wxSlider::DoGetBestClientSize(); + // If the base class is not using DoGetBestClientSize(), fallback to DoGetBestSize() + if (base_size == wxDefaultSize) + return wxDefaultSize; + return CorrectMinSize(base_size); +#endif +} + +wxSize DolphinSlider::DoGetBestSize() const +{ + return CorrectMinSize(wxSlider::DoGetBestSize()); +} + +wxSize DolphinSlider::CorrectMinSize(wxSize size) const +{ + wxSize default_length = FromDIP(wxSize(SLIDER_MIN_LENGTH, SLIDER_MIN_LENGTH)); + // GTK Styles sometimes don't return a default length. + // NOTE: Vertical is the dominant flag if both are set. + if (HasFlag(wxSL_VERTICAL)) + { + if (size.GetHeight() < default_length.GetHeight()) + { + size.SetHeight(default_length.GetHeight()); + } + } + else if (size.GetWidth() < default_length.GetWidth()) + { + size.SetWidth(default_length.GetWidth()); + } + return size; +} + +#ifdef __WXMSW__ +WXLRESULT DolphinSlider::MSWWindowProc(WXUINT msg, WXWPARAM wp, WXLPARAM lp) +{ + if (msg == WM_THEMECHANGED) + InvalidateBestSize(); + return wxSlider::MSWWindowProc(msg, wp, lp); +} +#endif diff --git a/Source/Core/DolphinWX/DolphinSlider.h b/Source/Core/DolphinWX/DolphinSlider.h new file mode 100644 index 0000000000..df3883e478 --- /dev/null +++ b/Source/Core/DolphinWX/DolphinSlider.h @@ -0,0 +1,51 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include <wx/slider.h> + +// wxSlider has several bugs, including not scaling with DPI. +// This extended slider class tries to paper over the flaws. +// NOTE: wxSL_LABELS is not supported because it doesn't work correctly. +class DolphinSlider : public wxSlider +{ +public: + DolphinSlider(); + ~DolphinSlider() override; + DolphinSlider(const DolphinSlider&) = delete; + + DolphinSlider(wxWindow* parent, wxWindowID id, int value, int min_value, int max_value, + const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, + long style = wxSL_HORIZONTAL, const wxValidator& validator = wxDefaultValidator, + const wxString& name = wxSliderNameStr) + { + Create(parent, id, value, min_value, max_value, pos, size, style, validator, name); + } + + DolphinSlider& operator=(const DolphinSlider&) = delete; + + bool Create(wxWindow* parent, wxWindowID id, int value, int min_value, int max_value, + const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, + long style = wxSL_HORIZONTAL, const wxValidator& validator = wxDefaultValidator, + const wxString& name = wxSliderNameStr); + +#ifdef __WXMSW__ + // For WM_THEMECHANGED to regenerate metrics + WXLRESULT MSWWindowProc(WXUINT msg, WXWPARAM wp, WXLPARAM lp) override; +#endif + +protected: + // DoGetBestSize() in wxMSW::wxSlider is borked. + // This is called by GetEffectiveMinSize() which is used by + // wxSizers to decide the size of the widget for generating layout. + wxSize DoGetBestClientSize() const override; + + // GTK Themes sometimes don't provide a default min size. + // Make other platforms consistent with Windows (i.e. min length = 100px) + wxSize DoGetBestSize() const override; + +private: + wxSize CorrectMinSize(wxSize size) const; +}; diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj b/Source/Core/DolphinWX/DolphinWX.vcxproj index 43bcc08c8a..d2e6fa30ba 100644 --- a/Source/Core/DolphinWX/DolphinWX.vcxproj +++ b/Source/Core/DolphinWX/DolphinWX.vcxproj @@ -89,6 +89,7 @@ <ClCompile Include="Debugger\RegisterWindow.cpp" /> <ClCompile Include="Debugger\WatchView.cpp" /> <ClCompile Include="Debugger\WatchWindow.cpp" /> + <ClCompile Include="DolphinSlider.cpp" /> <ClCompile Include="NetPlay\ChangeGameDialog.cpp" /> <ClCompile Include="NetPlay\MD5Dialog.cpp" /> <ClCompile Include="NetPlay\NetPlayLauncher.cpp" /> @@ -131,6 +132,7 @@ <ClInclude Include="Config\InterfaceConfigPane.h" /> <ClInclude Include="Config\PathConfigPane.h" /> <ClInclude Include="Config\WiiConfigPane.h" /> + <ClInclude Include="DolphinSlider.h" /> <ClInclude Include="NetPlay\ChangeGameDialog.h" /> <ClInclude Include="NetPlay\MD5Dialog.h" /> <ClInclude Include="NetPlay\NetPlayLauncher.h" /> diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj.filters b/Source/Core/DolphinWX/DolphinWX.vcxproj.filters index 2386e891db..4d597345d1 100644 --- a/Source/Core/DolphinWX/DolphinWX.vcxproj.filters +++ b/Source/Core/DolphinWX/DolphinWX.vcxproj.filters @@ -28,6 +28,9 @@ <Filter Include="GUI\Config"> <UniqueIdentifier>{9d8b4144-f335-4fa4-b995-852533298474}</UniqueIdentifier> </Filter> + <Filter Include="GUI\Widgets"> + <UniqueIdentifier>{a894e2e3-e577-4b65-8572-055699f23a49}</UniqueIdentifier> + </Filter> </ItemGroup> <ItemGroup> <ClCompile Include="Main.cpp" /> @@ -208,6 +211,9 @@ <ClCompile Include="NetPlay\NetPlayLauncher.cpp"> <Filter>GUI\NetPlay</Filter> </ClCompile> + <ClCompile Include="DolphinSlider.cpp"> + <Filter>GUI\Widgets</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="Main.h" /> @@ -381,6 +387,9 @@ <ClInclude Include="NetPlay\NetPlayLauncher.h"> <Filter>GUI\NetPlay</Filter> </ClInclude> + <ClInclude Include="DolphinSlider.h"> + <Filter>GUI\Widgets</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <Text Include="CMakeLists.txt" /> @@ -393,4 +402,4 @@ <ItemGroup> <Image Include="$(CoreDir)..\..\Installer\Dolphin.ico" /> </ItemGroup> -</Project> \ No newline at end of file +</Project> diff --git a/Source/Core/DolphinWX/FifoPlayerDlg.cpp b/Source/Core/DolphinWX/FifoPlayerDlg.cpp index aed9ad53b7..15c965cad7 100644 --- a/Source/Core/DolphinWX/FifoPlayerDlg.cpp +++ b/Source/Core/DolphinWX/FifoPlayerDlg.cpp @@ -66,149 +66,139 @@ FifoPlayerDlg::~FifoPlayerDlg() void FifoPlayerDlg::CreateGUIControls() { - wxBoxSizer* sMain; - sMain = new wxBoxSizer(wxVERTICAL); + const int space5 = FromDIP(5); m_Notebook = new wxNotebook(this, wxID_ANY); { m_PlayPage = new wxPanel(m_Notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* sPlayPage; - sPlayPage = new wxBoxSizer(wxVERTICAL); - - wxStaticBoxSizer* sPlayInfo; - sPlayInfo = - new wxStaticBoxSizer(new wxStaticBox(m_PlayPage, wxID_ANY, _("File Info")), wxVERTICAL); + // File Info m_NumFramesLabel = new wxStaticText(m_PlayPage, wxID_ANY, wxEmptyString); - m_NumFramesLabel->Wrap(-1); - sPlayInfo->Add(m_NumFramesLabel, 0, wxALL, 5); - m_CurrentFrameLabel = new wxStaticText(m_PlayPage, wxID_ANY, wxEmptyString); - m_CurrentFrameLabel->Wrap(-1); - sPlayInfo->Add(m_CurrentFrameLabel, 0, wxALL, 5); - m_NumObjectsLabel = new wxStaticText(m_PlayPage, wxID_ANY, wxEmptyString); - m_NumObjectsLabel->Wrap(-1); - sPlayInfo->Add(m_NumObjectsLabel, 0, wxALL, 5); - - sPlayPage->Add(sPlayInfo, 1, wxEXPAND, 5); - - wxStaticBoxSizer* sFrameRange; - sFrameRange = - new wxStaticBoxSizer(new wxStaticBox(m_PlayPage, wxID_ANY, _("Frame Range")), wxHORIZONTAL); + // Frame Range m_FrameFromLabel = new wxStaticText(m_PlayPage, wxID_ANY, _("From")); - m_FrameFromLabel->Wrap(-1); - sFrameRange->Add(m_FrameFromLabel, 0, wxALL, 5); - m_FrameFromCtrl = new wxSpinCtrl(m_PlayPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 10, 0); - sFrameRange->Add(m_FrameFromCtrl, 0, wxALL, 5); - m_FrameToLabel = new wxStaticText(m_PlayPage, wxID_ANY, _("To")); - m_FrameToLabel->Wrap(-1); - sFrameRange->Add(m_FrameToLabel, 0, wxALL, 5); - m_FrameToCtrl = new wxSpinCtrl(m_PlayPage, wxID_ANY, wxEmptyString, wxDefaultPosition, - wxSize(-1, -1), wxSP_ARROW_KEYS, 0, 10, 0); - sFrameRange->Add(m_FrameToCtrl, 0, wxALL, 5); - - sPlayPage->Add(sFrameRange, 0, wxEXPAND, 5); - - wxStaticBoxSizer* sObjectRange; - sObjectRange = new wxStaticBoxSizer(new wxStaticBox(m_PlayPage, wxID_ANY, _("Object Range")), - wxHORIZONTAL); + wxDefaultSize, wxSP_ARROW_KEYS, 0, 10, 0); + // Object Range m_ObjectFromLabel = new wxStaticText(m_PlayPage, wxID_ANY, _("From")); - m_ObjectFromLabel->Wrap(-1); - sObjectRange->Add(m_ObjectFromLabel, 0, wxALL, 5); - m_ObjectFromCtrl = new wxSpinCtrl(m_PlayPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 10000, 0); - sObjectRange->Add(m_ObjectFromCtrl, 0, wxALL, 5); - m_ObjectToLabel = new wxStaticText(m_PlayPage, wxID_ANY, _("To")); - m_ObjectToLabel->Wrap(-1); - sObjectRange->Add(m_ObjectToLabel, 0, wxALL, 5); - m_ObjectToCtrl = new wxSpinCtrl(m_PlayPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 10000, 0); - sObjectRange->Add(m_ObjectToCtrl, 0, wxALL, 5); - - sPlayPage->Add(sObjectRange, 0, wxEXPAND, 5); - - wxStaticBoxSizer* sPlayOptions; - sPlayOptions = new wxStaticBoxSizer( - new wxStaticBox(m_PlayPage, wxID_ANY, _("Playback Options")), wxVERTICAL); + // Playback Options m_EarlyMemoryUpdates = new wxCheckBox(m_PlayPage, wxID_ANY, _("Early Memory Updates")); - sPlayOptions->Add(m_EarlyMemoryUpdates, 0, wxALL, 5); - sPlayPage->Add(sPlayOptions, 0, wxEXPAND, 5); + wxStaticBoxSizer* sPlayInfo = new wxStaticBoxSizer(wxVERTICAL, m_PlayPage, _("File Info")); + sPlayInfo->AddSpacer(space5); + sPlayInfo->Add(m_NumFramesLabel, 0, wxLEFT | wxRIGHT, space5); + sPlayInfo->AddSpacer(space5); + sPlayInfo->Add(m_CurrentFrameLabel, 0, wxLEFT | wxRIGHT, space5); + sPlayInfo->AddSpacer(space5); + sPlayInfo->Add(m_NumObjectsLabel, 0, wxLEFT | wxRIGHT, space5); + sPlayInfo->AddSpacer(space5); + + wxStaticBoxSizer* sFrameRange = + new wxStaticBoxSizer(wxHORIZONTAL, m_PlayPage, _("Frame Range")); + sFrameRange->AddSpacer(space5); + sFrameRange->Add(m_FrameFromLabel, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, space5); + sFrameRange->AddSpacer(space5); + sFrameRange->Add(m_FrameFromCtrl, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, space5); + sFrameRange->AddSpacer(space5); + sFrameRange->Add(m_FrameToLabel, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, space5); + sFrameRange->AddSpacer(space5); + sFrameRange->Add(m_FrameToCtrl, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, space5); + sFrameRange->AddSpacer(space5); + + wxStaticBoxSizer* sObjectRange = + new wxStaticBoxSizer(wxHORIZONTAL, m_PlayPage, _("Object Range")); + sObjectRange->AddSpacer(space5); + sObjectRange->Add(m_ObjectFromLabel, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, space5); + sObjectRange->AddSpacer(space5); + sObjectRange->Add(m_ObjectFromCtrl, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, space5); + sObjectRange->AddSpacer(space5); + sObjectRange->Add(m_ObjectToLabel, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, space5); + sObjectRange->AddSpacer(space5); + sObjectRange->Add(m_ObjectToCtrl, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, space5); + sObjectRange->AddSpacer(space5); + + wxStaticBoxSizer* sPlayOptions = + new wxStaticBoxSizer(wxVERTICAL, m_PlayPage, _("Playback Options")); + sPlayOptions->AddSpacer(space5); + sPlayOptions->Add(m_EarlyMemoryUpdates, 0, wxLEFT | wxRIGHT, space5); + sPlayOptions->AddSpacer(space5); + + wxBoxSizer* sPlayPage = new wxBoxSizer(wxVERTICAL); + sPlayPage->Add(sPlayInfo, 1, wxEXPAND); + sPlayPage->Add(sFrameRange, 0, wxEXPAND); + sPlayPage->Add(sObjectRange, 0, wxEXPAND); + sPlayPage->Add(sPlayOptions, 0, wxEXPAND); sPlayPage->AddStretchSpacer(); m_PlayPage->SetSizer(sPlayPage); - m_PlayPage->Layout(); - sPlayPage->Fit(m_PlayPage); m_Notebook->AddPage(m_PlayPage, _("Play"), true); } { m_RecordPage = new wxPanel(m_Notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* sRecordPage; - sRecordPage = new wxBoxSizer(wxVERTICAL); - - wxStaticBoxSizer* sRecordInfo; - sRecordInfo = new wxStaticBoxSizer(new wxStaticBox(m_RecordPage, wxID_ANY, _("Recording Info")), - wxVERTICAL); + // Recording Info m_RecordingFifoSizeLabel = new wxStaticText(m_RecordPage, wxID_ANY, wxEmptyString); - m_RecordingFifoSizeLabel->Wrap(-1); - sRecordInfo->Add(m_RecordingFifoSizeLabel, 0, wxALL, 5); - m_RecordingMemSizeLabel = new wxStaticText(m_RecordPage, wxID_ANY, wxEmptyString); - m_RecordingMemSizeLabel->Wrap(-1); - sRecordInfo->Add(m_RecordingMemSizeLabel, 0, wxALL, 5); - m_RecordingFramesLabel = new wxStaticText(m_RecordPage, wxID_ANY, wxEmptyString); - m_RecordingFramesLabel->Wrap(-1); - sRecordInfo->Add(m_RecordingFramesLabel, 0, wxALL, 5); - - sRecordPage->Add(sRecordInfo, 0, wxEXPAND, 5); - - wxBoxSizer* sRecordButtons; - sRecordButtons = new wxBoxSizer(wxHORIZONTAL); + // Recording Buttons m_RecordStop = new wxButton(m_RecordPage, wxID_ANY, _("Record")); - sRecordButtons->Add(m_RecordStop, 0, wxALL, 5); - m_Save = new wxButton(m_RecordPage, wxID_ANY, _("Save")); - sRecordButtons->Add(m_Save, 0, wxALL, 5); - - sRecordPage->Add(sRecordButtons, 0, wxEXPAND, 5); - - wxStaticBoxSizer* sRecordingOptions; - sRecordingOptions = new wxStaticBoxSizer( - new wxStaticBox(m_RecordPage, wxID_ANY, _("Recording Options")), wxHORIZONTAL); + // Recording Options m_FramesToRecordLabel = new wxStaticText(m_RecordPage, wxID_ANY, _("Frames To Record")); - m_FramesToRecordLabel->Wrap(-1); - sRecordingOptions->Add(m_FramesToRecordLabel, 0, wxALL, 5); + m_FramesToRecordCtrl = + new wxSpinCtrl(m_RecordPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, + wxSP_ARROW_KEYS, 0, 10000, m_FramesToRecord); - wxString initialNum = wxString::Format("%d", m_FramesToRecord); - m_FramesToRecordCtrl = new wxSpinCtrl(m_RecordPage, wxID_ANY, initialNum, wxDefaultPosition, - wxDefaultSize, wxSP_ARROW_KEYS, 0, 10000, 1); - sRecordingOptions->Add(m_FramesToRecordCtrl, 0, wxALL, 5); + wxStaticBoxSizer* sRecordInfo = + new wxStaticBoxSizer(wxVERTICAL, m_RecordPage, _("Recording Info")); + sRecordInfo->AddSpacer(space5); + sRecordInfo->Add(m_RecordingFifoSizeLabel, 0, wxLEFT | wxRIGHT, space5); + sRecordInfo->AddSpacer(space5); + sRecordInfo->Add(m_RecordingMemSizeLabel, 0, wxLEFT | wxRIGHT, space5); + sRecordInfo->AddSpacer(space5); + sRecordInfo->Add(m_RecordingFramesLabel, 0, wxLEFT | wxRIGHT, space5); + sRecordInfo->AddSpacer(space5); - sRecordPage->Add(sRecordingOptions, 0, wxEXPAND, 5); - sRecordPage->AddStretchSpacer(); + wxBoxSizer* sRecordButtons = new wxBoxSizer(wxHORIZONTAL); + sRecordButtons->Add(m_RecordStop); + sRecordButtons->Add(m_Save, 0, wxLEFT, space5); + + wxStaticBoxSizer* sRecordingOptions = + new wxStaticBoxSizer(wxHORIZONTAL, m_RecordPage, _("Recording Options")); + sRecordingOptions->AddSpacer(space5); + sRecordingOptions->Add(m_FramesToRecordLabel, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, + space5); + sRecordingOptions->AddSpacer(space5); + sRecordingOptions->Add(m_FramesToRecordCtrl, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, + space5); + sRecordingOptions->AddSpacer(space5); + + wxBoxSizer* sRecordPage = new wxBoxSizer(wxVERTICAL); + sRecordPage->Add(sRecordInfo, 0, wxEXPAND); + sRecordPage->AddSpacer(space5); + sRecordPage->Add(sRecordButtons, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sRecordPage->AddSpacer(space5); + sRecordPage->Add(sRecordingOptions, 0, wxEXPAND); m_RecordPage->SetSizer(sRecordPage); - m_RecordPage->Layout(); - sRecordPage->Fit(m_RecordPage); m_Notebook->AddPage(m_RecordPage, _("Record"), false); } @@ -216,81 +206,86 @@ void FifoPlayerDlg::CreateGUIControls() { m_AnalyzePage = new wxPanel(m_Notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* sAnalyzePage; - sAnalyzePage = new wxBoxSizer(wxVERTICAL); - wxStaticBoxSizer* sFrameInfoSizer; - sFrameInfoSizer = - new wxStaticBoxSizer(new wxStaticBox(m_AnalyzePage, wxID_ANY, _("Frame Info")), wxVERTICAL); + // FIFO Content Lists + m_framesList = new wxListBox(m_AnalyzePage, wxID_ANY, wxDefaultPosition, + wxDLG_UNIT(this, wxSize(72, 185))); + m_objectsList = new wxListBox(m_AnalyzePage, wxID_ANY, wxDefaultPosition, + wxDLG_UNIT(this, wxSize(72, 185))); + m_objectCmdList = + new wxListBox(m_AnalyzePage, wxID_ANY, wxDefaultPosition, + wxDLG_UNIT(this, wxSize(144, 185)), wxArrayString(), wxLB_HSCROLL); - wxBoxSizer* sListsSizer = new wxBoxSizer(wxHORIZONTAL); + // Selected command breakdown + m_objectCmdInfo = new wxStaticText(m_AnalyzePage, wxID_ANY, wxEmptyString); - m_framesList = new wxListBox(m_AnalyzePage, wxID_ANY); - m_framesList->SetMinSize(wxSize(100, 250)); - sListsSizer->Add(m_framesList, 0, wxALL, 5); - - m_objectsList = new wxListBox(m_AnalyzePage, wxID_ANY); - m_objectsList->SetMinSize(wxSize(110, 250)); - sListsSizer->Add(m_objectsList, 0, wxALL, 5); - - m_objectCmdList = new wxListBox(m_AnalyzePage, wxID_ANY); - m_objectCmdList->SetMinSize(wxSize(175, 250)); - sListsSizer->Add(m_objectCmdList, 0, wxALL, 5); - - sFrameInfoSizer->Add(sListsSizer, 0, wxALL, 5); - - m_objectCmdInfo = new wxStaticText(m_AnalyzePage, wxID_ANY, wxString()); - sFrameInfoSizer->Add(m_objectCmdInfo, 0, wxALL, 5); - - sAnalyzePage->Add(sFrameInfoSizer, 0, wxEXPAND, 5); - - wxStaticBoxSizer* sSearchSizer = new wxStaticBoxSizer( - new wxStaticBox(m_AnalyzePage, wxID_ANY, _("Search current Object")), wxVERTICAL); - - wxBoxSizer* sSearchField = new wxBoxSizer(wxHORIZONTAL); - - sSearchField->Add(new wxStaticText(m_AnalyzePage, wxID_ANY, _("Search for hex Value:")), 0, - wxALIGN_CENTER_VERTICAL, 5); + // Search box + wxStaticText* search_label = + new wxStaticText(m_AnalyzePage, wxID_ANY, _("Search for hex Value:")); // TODO: ugh, wxValidator sucks - but we should use it anyway. m_searchField = new wxTextCtrl(m_AnalyzePage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); m_numResultsText = new wxStaticText(m_AnalyzePage, wxID_ANY, wxEmptyString); - sSearchField->Add(m_searchField, 0, wxALL, 5); - sSearchField->Add(m_numResultsText, 0, wxALIGN_CENTER_VERTICAL, 5); - - wxBoxSizer* sSearchButtons = new wxBoxSizer(wxHORIZONTAL); - + // Search buttons m_beginSearch = new wxButton(m_AnalyzePage, wxID_ANY, _("Search")); m_findNext = new wxButton(m_AnalyzePage, wxID_ANY, _("Find next")); m_findPrevious = new wxButton(m_AnalyzePage, wxID_ANY, _("Find previous")); ResetSearch(); - sSearchButtons->Add(m_beginSearch, 0, wxALL, 5); - sSearchButtons->Add(m_findNext, 0, wxALL, 5); - sSearchButtons->Add(m_findPrevious, 0, wxALL, 5); + wxBoxSizer* sListsSizer = new wxBoxSizer(wxHORIZONTAL); + sListsSizer->Add(m_framesList); + sListsSizer->Add(m_objectsList, 0, wxLEFT, space5); + sListsSizer->Add(m_objectCmdList, 1, wxLEFT, space5); - sSearchSizer->Add(sSearchField, 0, wxEXPAND, 5); - sSearchSizer->Add(sSearchButtons, 0, wxEXPAND, 5); + wxStaticBoxSizer* sFrameInfoSizer = + new wxStaticBoxSizer(wxVERTICAL, m_AnalyzePage, _("Frame Info")); + sFrameInfoSizer->AddSpacer(space5); + sFrameInfoSizer->Add(sListsSizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sFrameInfoSizer->AddSpacer(space5); + sFrameInfoSizer->Add(m_objectCmdInfo, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sFrameInfoSizer->AddSpacer(space5); - sAnalyzePage->Add(sSearchSizer, 0, wxEXPAND, 5); - sAnalyzePage->AddStretchSpacer(); + wxBoxSizer* sSearchField = new wxBoxSizer(wxHORIZONTAL); + sSearchField->Add(search_label, 0, wxALIGN_CENTER_VERTICAL); + sSearchField->Add(m_searchField, 0, wxLEFT, space5); + sSearchField->Add(m_numResultsText, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); + + wxBoxSizer* sSearchButtons = new wxBoxSizer(wxHORIZONTAL); + sSearchButtons->Add(m_beginSearch); + sSearchButtons->Add(m_findNext, 0, wxLEFT, space5); + sSearchButtons->Add(m_findPrevious, 0, wxLEFT, space5); + + wxStaticBoxSizer* sSearchSizer = + new wxStaticBoxSizer(wxVERTICAL, m_AnalyzePage, _("Search current Object")); + sSearchSizer->Add(sSearchField, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sSearchSizer->AddSpacer(space5); + sSearchSizer->Add(sSearchButtons, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sSearchSizer->AddSpacer(space5); + + wxBoxSizer* sAnalyzePage = new wxBoxSizer(wxVERTICAL); + sAnalyzePage->Add(sFrameInfoSizer, 0, wxEXPAND); + sAnalyzePage->Add(sSearchSizer, 0, wxEXPAND); m_AnalyzePage->SetSizer(sAnalyzePage); - m_AnalyzePage->Layout(); - sAnalyzePage->Fit(m_AnalyzePage); m_Notebook->AddPage(m_AnalyzePage, _("Analyze"), false); } - sMain->Add(m_Notebook, 1, wxEXPAND | wxALL, 5); - sMain->Add(CreateButtonSizer(wxCLOSE), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + wxStdDialogButtonSizer* close_btn_sizer = CreateStdDialogButtonSizer(wxCLOSE); + close_btn_sizer->GetCancelButton()->SetLabel(_("Close")); - SetSizer(sMain); - Layout(); - sMain->Fit(this); + wxBoxSizer* sMain = new wxBoxSizer(wxVERTICAL); + sMain->AddSpacer(space5); + sMain->Add(m_Notebook, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + sMain->AddSpacer(space5); + sMain->Add(close_btn_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sMain->AddSpacer(space5); - Center(wxBOTH); + SetLayoutAdaptationMode(wxDIALOG_ADAPTATION_MODE_ENABLED); + SetLayoutAdaptationLevel(wxDIALOG_ADAPTATION_STANDARD_SIZER); + SetSizerAndFit(sMain); + Center(); // Connect Events Bind(wxEVT_PAINT, &FifoPlayerDlg::OnPaint, this); @@ -302,7 +297,6 @@ void FifoPlayerDlg::CreateGUIControls() m_RecordStop->Bind(wxEVT_BUTTON, &FifoPlayerDlg::OnRecordStop, this); m_Save->Bind(wxEVT_BUTTON, &FifoPlayerDlg::OnSaveFile, this); m_FramesToRecordCtrl->Bind(wxEVT_SPINCTRL, &FifoPlayerDlg::OnNumFramesToRecord, this); - Bind(wxEVT_BUTTON, &FifoPlayerDlg::OnCloseClick, this); m_framesList->Bind(wxEVT_LISTBOX, &FifoPlayerDlg::OnFrameListSelectionChanged, this); m_objectsList->Bind(wxEVT_LISTBOX, &FifoPlayerDlg::OnObjectListSelectionChanged, this); @@ -808,11 +802,6 @@ void FifoPlayerDlg::OnObjectCmdListSelectionCopy(wxCommandEvent& WXUNUSED(event) } } -void FifoPlayerDlg::OnCloseClick(wxCommandEvent& WXUNUSED(event)) -{ - Hide(); -} - void FifoPlayerDlg::OnRecordingFinished(wxEvent&) { m_RecordStop->SetLabel(_("Record")); diff --git a/Source/Core/DolphinWX/FifoPlayerDlg.h b/Source/Core/DolphinWX/FifoPlayerDlg.h index 16f562e3c7..819e103f6b 100644 --- a/Source/Core/DolphinWX/FifoPlayerDlg.h +++ b/Source/Core/DolphinWX/FifoPlayerDlg.h @@ -39,7 +39,6 @@ private: void OnRecordStop(wxCommandEvent& event); void OnSaveFile(wxCommandEvent& event); void OnNumFramesToRecord(wxSpinEvent& event); - void OnCloseClick(wxCommandEvent& event); void OnBeginSearch(wxCommandEvent& event); void OnFindNextClick(wxCommandEvent& event); diff --git a/Source/Core/DolphinWX/Frame.cpp b/Source/Core/DolphinWX/Frame.cpp index 422867e904..5cfbc79937 100644 --- a/Source/Core/DolphinWX/Frame.cpp +++ b/Source/Core/DolphinWX/Frame.cpp @@ -85,9 +85,7 @@ CRenderFrame::CRenderFrame(wxFrame* parent, wxWindowID id, const wxString& title : wxFrame(parent, id, title, pos, size, style) { // Give it an icon - wxIcon IconTemp; - IconTemp.CopyFromBitmap(WxUtils::LoadResourceBitmap("Dolphin")); - SetIcon(IconTemp); + SetIcons(WxUtils::GetDolphinIconBundle()); DragAcceptFiles(true); Bind(wxEVT_DROP_FILES, &CRenderFrame::OnDropFiles, this); @@ -323,14 +321,9 @@ EVT_MOVE(CFrame::OnMove) EVT_HOST_COMMAND(wxID_ANY, CFrame::OnHostMessage) EVT_AUI_PANE_CLOSE(CFrame::OnPaneClose) -EVT_AUINOTEBOOK_PAGE_CLOSE(wxID_ANY, CFrame::OnNotebookPageClose) -EVT_AUINOTEBOOK_ALLOW_DND(wxID_ANY, CFrame::OnAllowNotebookDnD) -EVT_AUINOTEBOOK_PAGE_CHANGED(wxID_ANY, CFrame::OnNotebookPageChanged) -EVT_AUINOTEBOOK_TAB_RIGHT_UP(wxID_ANY, CFrame::OnTab) // Post events to child panels EVT_MENU_RANGE(IDM_INTERPRETER, IDM_ADDRBOX, CFrame::PostEvent) -EVT_TEXT(IDM_ADDRBOX, CFrame::PostEvent) END_EVENT_TABLE() @@ -381,22 +374,18 @@ static BOOL WINAPI s_ctrl_handler(DWORD fdwCtrlType) } #endif -CFrame::CFrame(wxFrame* parent, wxWindowID id, const wxString& title, const wxPoint& pos, - const wxSize& size, bool _UseDebugger, bool _BatchMode, bool ShowLogWindow, - long style) - : CRenderFrame(parent, id, title, pos, size, style), UseDebugger(_UseDebugger), - m_bBatchMode(_BatchMode) +CFrame::CFrame(wxFrame* parent, wxWindowID id, const wxString& title, wxRect geometry, + bool use_debugger, bool batch_mode, bool show_log_window, long style) + : CRenderFrame(parent, id, title, wxDefaultPosition, wxSize(800, 600), style), + UseDebugger(use_debugger), m_bBatchMode(batch_mode), + m_toolbar_bitmap_size(FromDIP(wxSize(32, 32))) { for (int i = 0; i <= IDM_CODE_WINDOW - IDM_LOG_WINDOW; i++) bFloatWindow[i] = false; - if (ShowLogWindow) + if (show_log_window) SConfig::GetInstance().m_InterfaceLogWindow = true; - // Start debugging maximized - if (UseDebugger) - this->Maximize(true); - // Debugger class if (UseDebugger) { @@ -487,15 +476,23 @@ CFrame::CFrame(wxFrame* parent, wxWindowID id, const wxString& title, const wxPo ToggleLogConfigWindow(true); } - // Set the size of the window after the UI has been built, but before we show it - SetSize(size); + // Setup the window size. + // This has to be done here instead of in Main because the Show() happens here. + SetMinSize(FromDIP(wxSize(400, 300))); + WxUtils::SetWindowSizeAndFitToScreen(this, geometry.GetPosition(), geometry.GetSize(), + FromDIP(wxSize(800, 600))); - // Show window - Show(); + // Start debugging maximized (Must be after the window has been positioned) + if (UseDebugger) + Maximize(true); // Commit m_Mgr->Update(); + // The window must be shown for m_XRRConfig to be created (wxGTK will not allocate X11 + // resources until the window is shown for the first time). + Show(); + #ifdef _WIN32 SetToolTip(""); GetToolTip()->SetAutoPop(25000); @@ -639,11 +636,10 @@ void CFrame::OnClose(wxCloseEvent& event) } else { - // Close the log window now so that its settings are saved - if (m_LogWindow) - m_LogWindow->Close(); - m_LogWindow = nullptr; + m_LogWindow->SaveSettings(); } + if (m_LogWindow) + m_LogWindow->RemoveAllListeners(); // Uninit m_Mgr->UnInit(); @@ -680,7 +676,8 @@ void CFrame::OnResize(wxSizeEvent& event) { event.Skip(); - if (!IsMaximized() && !(SConfig::GetInstance().bRenderToMain && RendererIsFullscreen()) && + if (!IsMaximized() && !IsIconized() && + !(SConfig::GetInstance().bRenderToMain && RendererIsFullscreen()) && !(Core::GetState() != Core::CORE_UNINITIALIZED && SConfig::GetInstance().bRenderToMain && SConfig::GetInstance().bRenderWindowAutoSize)) { @@ -740,6 +737,11 @@ void CFrame::OnHostMessage(wxCommandEvent& event) { switch (event.GetId()) { + case IDM_UPDATE_DISASM_DIALOG: // For breakpoints causing pausing + if (!g_pCodeWindow || Core::GetState() != Core::CORE_PAUSE) + return; + // fallthrough + case IDM_UPDATE_GUI: UpdateGUI(); break; @@ -821,33 +823,29 @@ void CFrame::OnHostMessage(wxCommandEvent& event) void CFrame::OnRenderWindowSizeRequest(int width, int height) { - if (!Core::IsRunning() || !SConfig::GetInstance().bRenderWindowAutoSize || + if (!SConfig::GetInstance().bRenderWindowAutoSize || !Core::IsRunning() || RendererIsFullscreen() || m_RenderFrame->IsMaximized()) return; - int old_width, old_height, log_width = 0, log_height = 0; - m_RenderFrame->GetClientSize(&old_width, &old_height); + wxSize requested_size(width, height); + // Convert to window pixels, since the size is from the backend it will be in framebuffer px. + requested_size *= 1.0 / m_RenderFrame->GetContentScaleFactor(); + wxSize old_size; - // Add space for the log/console/debugger window - if (SConfig::GetInstance().bRenderToMain && (SConfig::GetInstance().m_InterfaceLogWindow || - SConfig::GetInstance().m_InterfaceLogConfigWindow) && - !m_Mgr->GetPane("Pane 1").IsFloating()) + if (!SConfig::GetInstance().bRenderToMain) { - switch (m_Mgr->GetPane("Pane 1").dock_direction) - { - case wxAUI_DOCK_LEFT: - case wxAUI_DOCK_RIGHT: - log_width = m_Mgr->GetPane("Pane 1").rect.GetWidth(); - break; - case wxAUI_DOCK_TOP: - case wxAUI_DOCK_BOTTOM: - log_height = m_Mgr->GetPane("Pane 1").rect.GetHeight(); - break; - } + old_size = m_RenderFrame->GetClientSize(); + } + else + { + // Resize for the render panel only, this implicitly retains space for everything else + // (i.e. log panel, toolbar, statusbar, etc) without needing to compute for them. + old_size = m_RenderParent->GetSize(); } - if (old_width != width + log_width || old_height != height + log_height) - m_RenderFrame->SetClientSize(width + log_width, height + log_height); + wxSize diff = requested_size - old_size; + if (diff != wxSize()) + m_RenderFrame->SetSize(m_RenderFrame->GetSize() + diff); } bool CFrame::RendererHasFocus() @@ -930,7 +928,7 @@ void CFrame::OnGameListCtrlItemActivated(wxListEvent& WXUNUSED(event)) GetMenuBar()->FindItem(IDM_LIST_WORLD)->Check(true); GetMenuBar()->FindItem(IDM_LIST_UNKNOWN)->Check(true); - m_GameListCtrl->Update(); + UpdateGameList(); } else if (!m_GameListCtrl->GetISO(0)) { diff --git a/Source/Core/DolphinWX/Frame.h b/Source/Core/DolphinWX/Frame.h index 33857e7a9f..2f2f4ce6aa 100644 --- a/Source/Core/DolphinWX/Frame.h +++ b/Source/Core/DolphinWX/Frame.h @@ -46,7 +46,7 @@ class CRenderFrame : public wxFrame public: CRenderFrame(wxFrame* parent, wxWindowID id = wxID_ANY, const wxString& title = "Dolphin", const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, - long style = wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE); + long style = wxDEFAULT_FRAME_STYLE); bool ShowFullScreen(bool show, long style = wxFULLSCREEN_ALL) override; @@ -63,8 +63,8 @@ class CFrame : public CRenderFrame { public: CFrame(wxFrame* parent, wxWindowID id = wxID_ANY, const wxString& title = "Dolphin", - const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, - bool _UseDebugger = false, bool _BatchMode = false, bool ShowLogWindow = false, + wxRect geometry = wxDefaultSize, bool use_debugger = false, bool batch_mode = false, + bool show_log_window = false, long style = wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE); virtual ~CFrame(); @@ -113,6 +113,7 @@ public: const CGameListCtrl* GetGameListCtrl() const; wxMenuBar* GetMenuBar() const override; + const wxSize& GetToolbarBitmapSize() const; // Needed before the toolbar exists #ifdef __WXGTK__ Common::Event panic_event; @@ -129,7 +130,7 @@ public: wxToolBar* m_ToolBar = nullptr; // AUI wxAuiManager* m_Mgr = nullptr; - bool bFloatWindow[IDM_CODE_WINDOW - IDM_LOG_WINDOW + 1]; + bool bFloatWindow[IDM_DEBUG_WINDOW_LIST_END - IDM_DEBUG_WINDOW_LIST_START] = {}; // Perspectives (Should find a way to make all of this private) void DoAddPage(wxWindow* Win, int i, bool Float); @@ -191,6 +192,7 @@ private: wxTimer m_poll_hotkey_timer; wxTimer m_handle_signal_timer; + wxSize m_toolbar_bitmap_size; wxBitmap m_Bitmaps[EToolbar_Max]; wxMenuBar* m_menubar_shadow = nullptr; @@ -209,12 +211,12 @@ private: // Perspectives void AddRemoveBlankPage(); - void OnNotebookPageClose(wxAuiNotebookEvent& event); - void OnAllowNotebookDnD(wxAuiNotebookEvent& event); + void OnNotebookAllowDnD(wxAuiNotebookEvent& event); void OnNotebookPageChanged(wxAuiNotebookEvent& event); + void OnNotebookPageClose(wxAuiNotebookEvent& event); + void OnNotebookTabRightUp(wxAuiNotebookEvent& event); void OnFloatWindow(wxCommandEvent& event); void ToggleFloatWindow(int Id); - void OnTab(wxAuiNotebookEvent& event); int GetNotebookAffiliation(wxWindowID Id); void ClosePages(); void CloseAllNotebooks(); @@ -226,7 +228,6 @@ private: // Float window void DoUnfloatPage(int Id); void OnFloatingPageClosed(wxCloseEvent& event); - void OnFloatingPageSize(wxSizeEvent& event); void DoFloatNotebookPage(wxWindowID Id); wxFrame* CreateParentFrame(wxWindowID Id = wxID_ANY, const wxString& title = "", wxWindow* = nullptr); diff --git a/Source/Core/DolphinWX/FrameAui.cpp b/Source/Core/DolphinWX/FrameAui.cpp index e5ea077a28..4bc4e50206 100644 --- a/Source/Core/DolphinWX/FrameAui.cpp +++ b/Source/Core/DolphinWX/FrameAui.cpp @@ -152,41 +152,22 @@ void CFrame::ToggleLogConfigWindow(bool bShow) void CFrame::OnToggleWindow(wxCommandEvent& event) { - bool bShow = GetMenuBar()->IsChecked(event.GetId()); + bool show = GetMenuBar()->IsChecked(event.GetId()); switch (event.GetId()) { case IDM_LOG_WINDOW: if (!g_pCodeWindow) - SConfig::GetInstance().m_InterfaceLogWindow = bShow; - ToggleLogWindow(bShow); + SConfig::GetInstance().m_InterfaceLogWindow = show; + ToggleLogWindow(show); break; case IDM_LOG_CONFIG_WINDOW: if (!g_pCodeWindow) - SConfig::GetInstance().m_InterfaceLogConfigWindow = bShow; - ToggleLogConfigWindow(bShow); - break; - case IDM_REGISTER_WINDOW: - g_pCodeWindow->ToggleRegisterWindow(bShow); - break; - case IDM_WATCH_WINDOW: - g_pCodeWindow->ToggleWatchWindow(bShow); - break; - case IDM_BREAKPOINT_WINDOW: - g_pCodeWindow->ToggleBreakPointWindow(bShow); - break; - case IDM_MEMORY_WINDOW: - g_pCodeWindow->ToggleMemoryWindow(bShow); - break; - case IDM_JIT_WINDOW: - g_pCodeWindow->ToggleJitWindow(bShow); - break; - case IDM_SOUND_WINDOW: - g_pCodeWindow->ToggleSoundWindow(bShow); - break; - case IDM_VIDEO_WINDOW: - g_pCodeWindow->ToggleVideoWindow(bShow); + SConfig::GetInstance().m_InterfaceLogConfigWindow = show; + ToggleLogConfigWindow(show); break; + default: + g_pCodeWindow->TogglePanel(event.GetId(), show); } } @@ -199,20 +180,21 @@ void CFrame::ClosePages() if (g_pCodeWindow) { - g_pCodeWindow->ToggleCodeWindow(false); - g_pCodeWindow->ToggleRegisterWindow(false); - g_pCodeWindow->ToggleWatchWindow(false); - g_pCodeWindow->ToggleBreakPointWindow(false); - g_pCodeWindow->ToggleMemoryWindow(false); - g_pCodeWindow->ToggleJitWindow(false); - g_pCodeWindow->ToggleSoundWindow(false); - g_pCodeWindow->ToggleVideoWindow(false); + for (int i = IDM_REGISTER_WINDOW; i < IDM_DEBUG_WINDOW_LIST_END; ++i) + { + g_pCodeWindow->TogglePanel(i, false); + } } } void CFrame::OnNotebookPageChanged(wxAuiNotebookEvent& event) { - event.Skip(); + // Event is intended for someone else + if (event.GetPropagatedFrom() != nullptr) + { + event.Skip(); + return; + } if (!g_pCodeWindow) return; @@ -230,39 +212,45 @@ void CFrame::OnNotebookPageChanged(wxAuiNotebookEvent& event) void CFrame::OnNotebookPageClose(wxAuiNotebookEvent& event) { + // Event is intended for someone else + if (event.GetPropagatedFrom() != nullptr) + { + event.Skip(); + return; + } + // Override event event.Veto(); - wxAuiNotebook* Ctrl = (wxAuiNotebook*)event.GetEventObject(); + wxAuiNotebook* nb = static_cast<wxAuiNotebook*>(event.GetEventObject()); + int page_id = nb->GetPage(event.GetSelection())->GetId(); - if (Ctrl->GetPage(event.GetSelection())->GetId() == IDM_LOG_WINDOW) - ToggleLogWindow(false); - if (Ctrl->GetPage(event.GetSelection())->GetId() == IDM_LOG_CONFIG_WINDOW) - ToggleLogConfigWindow(false); - if (Ctrl->GetPage(event.GetSelection())->GetId() == IDM_REGISTER_WINDOW) - g_pCodeWindow->ToggleRegisterWindow(false); - if (Ctrl->GetPage(event.GetSelection())->GetId() == IDM_WATCH_WINDOW) - g_pCodeWindow->ToggleWatchWindow(false); - if (Ctrl->GetPage(event.GetSelection())->GetId() == IDM_BREAKPOINT_WINDOW) - g_pCodeWindow->ToggleBreakPointWindow(false); - if (Ctrl->GetPage(event.GetSelection())->GetId() == IDM_JIT_WINDOW) - g_pCodeWindow->ToggleJitWindow(false); - if (Ctrl->GetPage(event.GetSelection())->GetId() == IDM_MEMORY_WINDOW) - g_pCodeWindow->ToggleMemoryWindow(false); - if (Ctrl->GetPage(event.GetSelection())->GetId() == IDM_SOUND_WINDOW) - g_pCodeWindow->ToggleSoundWindow(false); - if (Ctrl->GetPage(event.GetSelection())->GetId() == IDM_VIDEO_WINDOW) - g_pCodeWindow->ToggleVideoWindow(false); + switch (page_id) + { + case IDM_LOG_WINDOW: + case IDM_LOG_CONFIG_WINDOW: + { + GetMenuBar()->Check(page_id, !GetMenuBar()->IsChecked(page_id)); + wxCommandEvent ev(wxEVT_MENU, page_id); + OnToggleWindow(ev); + break; + } + case IDM_CODE_WINDOW: + break; // Code Window is not allowed to be closed + default: + // Check for the magic empty panel. + if (nb->GetPageText(event.GetSelection()).IsSameAs("<>")) + break; + + g_pCodeWindow->TogglePanel(page_id, false); + } } void CFrame::OnFloatingPageClosed(wxCloseEvent& event) { - ToggleFloatWindow(event.GetId() - IDM_LOG_WINDOW_PARENT + IDM_FLOAT_LOG_WINDOW); -} + // TODO: This is a good place to save the window size and position to an INI -void CFrame::OnFloatingPageSize(wxSizeEvent& event) -{ - event.Skip(); + ToggleFloatWindow(event.GetId() - IDM_LOG_WINDOW_PARENT + IDM_FLOAT_LOG_WINDOW); } void CFrame::OnFloatWindow(wxCommandEvent& event) @@ -320,9 +308,15 @@ void CFrame::DoUnfloatPage(int Id) Win->Destroy(); } -void CFrame::OnTab(wxAuiNotebookEvent& event) +void CFrame::OnNotebookTabRightUp(wxAuiNotebookEvent& event) { - event.Skip(); + // Event is intended for someone else + if (event.GetPropagatedFrom() != nullptr) + { + event.Skip(); + return; + } + if (!g_pCodeWindow) return; @@ -354,10 +348,21 @@ void CFrame::OnTab(wxAuiNotebookEvent& event) PopupMenu(&MenuPopup, Pt); } -void CFrame::OnAllowNotebookDnD(wxAuiNotebookEvent& event) +void CFrame::OnNotebookAllowDnD(wxAuiNotebookEvent& event) { - event.Skip(); - event.Allow(); + // NOTE: This event was sent FROM the source notebook TO the destination notebook so + // all the member variables are related to the source, we can't get the drop target. + // NOTE: This function is "part of the internal interface" but there's no clean alternative. + if (event.GetPropagatedFrom() != nullptr) + { + // Drop target was one of the notebook's children, we don't care about this event. + event.Skip(); + return; + } + // Since the destination is one of our own notebooks, make sure the source is as well. + // If the source is some other panel, leave the event in the default reject state. + if (m_Mgr->GetPane(event.GetDragSource()).window) + event.Allow(); } void CFrame::ShowResizePane() @@ -391,12 +396,7 @@ void CFrame::ShowResizePane() void CFrame::TogglePane() { // Get the first notebook - wxAuiNotebook* NB = nullptr; - for (u32 i = 0; i < m_Mgr->GetAllPanes().GetCount(); i++) - { - if (m_Mgr->GetAllPanes()[i].window->IsKindOf(CLASSINFO(wxAuiNotebook))) - NB = (wxAuiNotebook*)m_Mgr->GetAllPanes()[i].window; - } + wxAuiNotebook* NB = GetNotebookFromId(0); if (NB) { @@ -450,6 +450,7 @@ void CFrame::DoRemovePage(wxWindow* Win, bool bHide) { Win->Destroy(); } + break; } } } @@ -468,13 +469,17 @@ void CFrame::DoAddPage(wxWindow* Win, int i, bool Float) i = 0; // The page was already previously added, no need to add it again. - if (Win && GetNotebookFromId(i)->GetPageIndex(Win) != wxNOT_FOUND) + if (GetNotebookFromId(i)->GetPageIndex(Win) != wxNOT_FOUND) return; if (!Float) + { GetNotebookFromId(i)->AddPage(Win, Win->GetName(), true); + } else + { CreateParentFrame(Win->GetId() + IDM_LOG_WINDOW_PARENT - IDM_LOG_WINDOW, Win->GetName(), Win); + } } void CFrame::PopulateSavedPerspectives() @@ -664,8 +669,7 @@ void CFrame::SetPaneSize() if (Perspectives.size() <= ActivePerspective) return; - int iClientX = GetSize().GetX(); - int iClientY = GetSize().GetY(); + wxSize client_size = GetClientSize(); for (u32 i = 0, j = 0; i < m_Mgr->GetAllPanes().GetCount(); i++) { @@ -687,8 +691,8 @@ void CFrame::SetPaneSize() H = MathUtil::Clamp<u32>(H, 5, 95); // Convert percentages to pixel lengths - W = (W * iClientX) / 100; - H = (H * iClientY) / 100; + W = (W * client_size.GetWidth()) / 100; + H = (H * client_size.GetHeight()) / 100; m_Mgr->GetAllPanes()[i].BestSize(W, H).MinSize(W, H); j++; @@ -815,7 +819,7 @@ void CFrame::UpdateCurrentPerspective() current->Perspective = m_Mgr->SavePerspective(); // Get client size - int iClientX = GetSize().GetX(), iClientY = GetSize().GetY(); + wxSize client_size = GetClientSize(); current->Width.clear(); current->Height.clear(); for (u32 i = 0; i < m_Mgr->GetAllPanes().GetCount(); i++) @@ -823,10 +827,10 @@ void CFrame::UpdateCurrentPerspective() if (!m_Mgr->GetAllPanes()[i].window->IsKindOf(CLASSINFO(wxAuiToolBar))) { // Save width and height as a percentage of the client width and height - current->Width.push_back((m_Mgr->GetAllPanes()[i].window->GetClientSize().GetX() * 100) / - iClientX); - current->Height.push_back((m_Mgr->GetAllPanes()[i].window->GetClientSize().GetY() * 100) / - iClientY); + current->Width.push_back((m_Mgr->GetAllPanes()[i].window->GetSize().GetX() * 100) / + client_size.GetWidth()); + current->Height.push_back((m_Mgr->GetAllPanes()[i].window->GetSize().GetY() * 100) / + client_size.GetHeight()); } } } @@ -952,23 +956,40 @@ wxFrame* CFrame::CreateParentFrame(wxWindowID Id, const wxString& Title, wxWindo m_MainSizer->Add(Child, 1, wxEXPAND); + // If the tab is not the one currently being shown to the user then it will + // be hidden. Make sure it is being shown. + Child->Show(); + Frame->Bind(wxEVT_CLOSE_WINDOW, &CFrame::OnFloatingPageClosed, this); + // TODO: This is a good place to load window position and size settings from an INI + // Main sizer - Frame->SetSizer(m_MainSizer); - // Minimum frame size - Frame->SetMinSize(wxSize(200, 200)); + Frame->SetSizerAndFit(m_MainSizer); Frame->Show(); return Frame; } wxAuiNotebook* CFrame::CreateEmptyNotebook() { - const long NOTEBOOK_STYLE = wxAUI_NB_TOP | wxAUI_NB_TAB_SPLIT | wxAUI_NB_TAB_MOVE | - wxAUI_NB_TAB_EXTERNAL_MOVE | wxAUI_NB_SCROLL_BUTTONS | - wxAUI_NB_WINDOWLIST_BUTTON | wxNO_BORDER; + static constexpr long NOTEBOOK_STYLE = wxAUI_NB_TOP | wxAUI_NB_TAB_SPLIT | wxAUI_NB_TAB_MOVE | + wxAUI_NB_CLOSE_BUTTON | wxAUI_NB_TAB_EXTERNAL_MOVE | + wxAUI_NB_SCROLL_BUTTONS | wxAUI_NB_WINDOWLIST_BUTTON | + wxNO_BORDER; - return new wxAuiNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, NOTEBOOK_STYLE); + auto* nb = new wxAuiNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, NOTEBOOK_STYLE); + + // wxAuiNotebookEvent is derived from wxCommandEvent so they bubble up from child panels. + // This is a problem if the panels contain their own AUI Notebooks like DSPDebuggerLLE + // since we receive its events as though they came from our own children which we do + // not want to deal with. Binding directly to our notebooks and ignoring any event that + // has been propagated from somewhere else resolves it. + nb->Bind(wxEVT_AUINOTEBOOK_ALLOW_DND, &CFrame::OnNotebookAllowDnD, this); + nb->Bind(wxEVT_AUINOTEBOOK_PAGE_CHANGED, &CFrame::OnNotebookPageChanged, this); + nb->Bind(wxEVT_AUINOTEBOOK_PAGE_CLOSE, &CFrame::OnNotebookPageClose, this); + nb->Bind(wxEVT_AUINOTEBOOK_TAB_RIGHT_UP, &CFrame::OnNotebookTabRightUp, this); + + return nb; } void CFrame::AddRemoveBlankPage() diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index 29b8bca723..52bca00dc2 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include <array> #include <cstdarg> #include <cstdio> #include <mutex> @@ -77,21 +78,6 @@ #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoConfig.h" -#ifdef _WIN32 -#ifndef SM_XVIRTUALSCREEN -#define SM_XVIRTUALSCREEN 76 -#endif -#ifndef SM_YVIRTUALSCREEN -#define SM_YVIRTUALSCREEN 77 -#endif -#ifndef SM_CXVIRTUALSCREEN -#define SM_CXVIRTUALSCREEN 78 -#endif -#ifndef SM_CYVIRTUALSCREEN -#define SM_CYVIRTUALSCREEN 79 -#endif -#endif - class InputConfig; class wxFrame; @@ -108,6 +94,11 @@ wxMenuBar* CFrame::GetMenuBar() const } } +const wxSize& CFrame::GetToolbarBitmapSize() const +{ + return m_toolbar_bitmap_size; +} + // Create menu items // --------------------- wxMenuBar* CFrame::CreateMenu() @@ -528,9 +519,6 @@ wxString CFrame::GetMenuLabel(int Id) // --------------------- void CFrame::PopulateToolbar(wxToolBar* ToolBar) { - int w = m_Bitmaps[Toolbar_FileOpen].GetWidth(), h = m_Bitmaps[Toolbar_FileOpen].GetHeight(); - ToolBar->SetToolBitmapSize(wxSize(w, h)); - WxUtils::AddToolbarButton(ToolBar, wxID_OPEN, _("Open"), m_Bitmaps[Toolbar_FileOpen], _("Open file...")); WxUtils::AddToolbarButton(ToolBar, wxID_REFRESH, _("Refresh"), m_Bitmaps[Toolbar_Refresh], @@ -554,7 +542,7 @@ void CFrame::PopulateToolbar(wxToolBar* ToolBar) // Delete and recreate the toolbar void CFrame::RecreateToolbar() { - static const long TOOLBAR_STYLE = wxTB_DEFAULT_STYLE | wxTB_TEXT | wxTB_FLAT; + static constexpr long TOOLBAR_STYLE = wxTB_DEFAULT_STYLE | wxTB_TEXT | wxTB_FLAT; if (m_ToolBar != nullptr) { @@ -563,6 +551,7 @@ void CFrame::RecreateToolbar() } m_ToolBar = CreateToolBar(TOOLBAR_STYLE, wxID_ANY); + m_ToolBar->SetToolBitmapSize(m_toolbar_bitmap_size); if (g_pCodeWindow) { @@ -580,18 +569,11 @@ void CFrame::RecreateToolbar() void CFrame::InitBitmaps() { - auto const dir = StrToWxStr(File::GetThemeDir(SConfig::GetInstance().theme_name)); - - m_Bitmaps[Toolbar_FileOpen].LoadFile(dir + "open.png", wxBITMAP_TYPE_PNG); - m_Bitmaps[Toolbar_Refresh].LoadFile(dir + "refresh.png", wxBITMAP_TYPE_PNG); - m_Bitmaps[Toolbar_Play].LoadFile(dir + "play.png", wxBITMAP_TYPE_PNG); - m_Bitmaps[Toolbar_Stop].LoadFile(dir + "stop.png", wxBITMAP_TYPE_PNG); - m_Bitmaps[Toolbar_Pause].LoadFile(dir + "pause.png", wxBITMAP_TYPE_PNG); - m_Bitmaps[Toolbar_ConfigMain].LoadFile(dir + "config.png", wxBITMAP_TYPE_PNG); - m_Bitmaps[Toolbar_ConfigGFX].LoadFile(dir + "graphics.png", wxBITMAP_TYPE_PNG); - m_Bitmaps[Toolbar_Controller].LoadFile(dir + "classic.png", wxBITMAP_TYPE_PNG); - m_Bitmaps[Toolbar_Screenshot].LoadFile(dir + "screenshot.png", wxBITMAP_TYPE_PNG); - m_Bitmaps[Toolbar_FullScreen].LoadFile(dir + "fullscreen.png", wxBITMAP_TYPE_PNG); + static constexpr std::array<const char* const, EToolbar_Max> s_image_names{ + {"open", "refresh", "play", "stop", "pause", "screenshot", "fullscreen", "config", "graphics", + "classic"}}; + for (std::size_t i = 0; i < s_image_names.size(); ++i) + m_Bitmaps[i] = WxUtils::LoadScaledThemeBitmap(s_image_names[i], this, m_toolbar_bitmap_size); // Update in case the bitmap has been updated if (m_ToolBar != nullptr) @@ -606,7 +588,7 @@ void CFrame::OpenGeneralConfiguration(int tab) HotkeyManagerEmu::Enable(false); if (config_main.ShowModal() == wxID_OK) - m_GameListCtrl->Update(); + UpdateGameList(); HotkeyManagerEmu::Enable(true); UpdateGUI(); @@ -660,10 +642,10 @@ void CFrame::BootGame(const std::string& filename) StartGame(bootfile); if (UseDebugger && g_pCodeWindow) { - if (g_pCodeWindow->m_WatchWindow) - g_pCodeWindow->m_WatchWindow->LoadAll(); - if (g_pCodeWindow->m_BreakpointWindow) - g_pCodeWindow->m_BreakpointWindow->LoadAll(); + if (g_pCodeWindow->HasPanel<CWatchWindow>()) + g_pCodeWindow->GetPanel<CWatchWindow>()->LoadAll(); + if (g_pCodeWindow->HasPanel<CBreakPointWindow>()) + g_pCodeWindow->GetPanel<CBreakPointWindow>()->LoadAll(); } } } @@ -860,7 +842,7 @@ void CFrame::OnPlay(wxCommandEvent& WXUNUSED(event)) wxThread::Sleep(20); g_pCodeWindow->JumpToAddress(PC); - g_pCodeWindow->Update(); + g_pCodeWindow->Repopulate(); // Update toolbar with Play/Pause status UpdateGUI(); } @@ -987,36 +969,29 @@ void CFrame::StartGame(const std::string& filename) } else { - wxPoint position(SConfig::GetInstance().iRenderWindowXPos, - SConfig::GetInstance().iRenderWindowYPos); -#ifdef __APPLE__ - // On OS X, the render window's title bar is not visible, - // and the window therefore not easily moved, when the - // position is 0,0. Weed out the 0's from existing configs. - if (position == wxPoint(0, 0)) - position = wxDefaultPosition; -#endif + wxRect window_geometry( + SConfig::GetInstance().iRenderWindowXPos, SConfig::GetInstance().iRenderWindowYPos, + SConfig::GetInstance().iRenderWindowWidth, SConfig::GetInstance().iRenderWindowHeight); + // Set window size in framebuffer pixels since the 3D rendering will be operating at + // that level. + wxSize default_size{wxSize(640, 480) * (1.0 / GetContentScaleFactor())}; + m_RenderFrame = new CRenderFrame(this, wxID_ANY, _("Dolphin"), wxDefaultPosition, default_size); + + // Convert ClientSize coordinates to frame sizes. + wxSize decoration_fudge = m_RenderFrame->GetSize() - m_RenderFrame->GetClientSize(); + default_size += decoration_fudge; + if (!window_geometry.IsEmpty()) + window_geometry.SetSize(window_geometry.GetSize() + decoration_fudge); + + WxUtils::SetWindowSizeAndFitToScreen(m_RenderFrame, window_geometry.GetPosition(), + window_geometry.GetSize(), default_size); - wxSize size(SConfig::GetInstance().iRenderWindowWidth, - SConfig::GetInstance().iRenderWindowHeight); -#ifdef _WIN32 - // Out of desktop check - int leftPos = GetSystemMetrics(SM_XVIRTUALSCREEN); - int topPos = GetSystemMetrics(SM_YVIRTUALSCREEN); - int width = GetSystemMetrics(SM_CXVIRTUALSCREEN); - int height = GetSystemMetrics(SM_CYVIRTUALSCREEN); - if ((leftPos + width) < (position.x + size.GetWidth()) || leftPos > position.x || - (topPos + height) < (position.y + size.GetHeight()) || topPos > position.y) - position.x = position.y = wxDefaultCoord; -#endif - m_RenderFrame = new CRenderFrame((wxFrame*)this, wxID_ANY, _("Dolphin"), position); if (SConfig::GetInstance().bKeepWindowOnTop) m_RenderFrame->SetWindowStyle(m_RenderFrame->GetWindowStyle() | wxSTAY_ON_TOP); else m_RenderFrame->SetWindowStyle(m_RenderFrame->GetWindowStyle() & ~wxSTAY_ON_TOP); m_RenderFrame->SetBackgroundColour(*wxBLACK); - m_RenderFrame->SetClientSize(size.GetWidth(), size.GetHeight()); m_RenderFrame->Bind(wxEVT_CLOSE_WINDOW, &CFrame::OnRenderParentClose, this); m_RenderFrame->Bind(wxEVT_ACTIVATE, &CFrame::OnActive, this); m_RenderFrame->Bind(wxEVT_MOVE, &CFrame::OnRenderParentMove, this); @@ -1035,7 +1010,7 @@ void CFrame::StartGame(const std::string& filename) m_RenderFrame->EnableFullScreenView(true); #endif - wxBeginBusyCursor(); + wxBusyCursor hourglass; DoFullscreen(SConfig::GetInstance().bFullscreen); @@ -1045,6 +1020,7 @@ void CFrame::StartGame(const std::string& filename) // Destroy the renderer frame when not rendering to main if (!SConfig::GetInstance().bRenderToMain) m_RenderFrame->Destroy(); + m_RenderFrame = nullptr; m_RenderParent = nullptr; m_bGameLoading = false; UpdateGUI(); @@ -1076,8 +1052,6 @@ void CFrame::StartGame(const std::string& filename) wxTheApp->Bind(wxEVT_KILL_FOCUS, &CFrame::OnFocusChange, this); m_RenderParent->Bind(wxEVT_SIZE, &CFrame::OnRenderParentResize, this); } - - wxEndBusyCursor(); } void CFrame::OnBootDrive(wxCommandEvent& event) @@ -1088,10 +1062,7 @@ void CFrame::OnBootDrive(wxCommandEvent& event) // Refresh the file list and browse for a favorites directory void CFrame::OnRefresh(wxCommandEvent& WXUNUSED(event)) { - if (m_GameListCtrl) - { - m_GameListCtrl->Update(); - } + UpdateGameList(); } // Create screenshot @@ -1194,18 +1165,15 @@ void CFrame::DoStop() if (UseDebugger && g_pCodeWindow) { - if (g_pCodeWindow->m_WatchWindow) - { - g_pCodeWindow->m_WatchWindow->SaveAll(); - PowerPC::watches.Clear(); - } - if (g_pCodeWindow->m_BreakpointWindow) - { - g_pCodeWindow->m_BreakpointWindow->SaveAll(); - PowerPC::breakpoints.Clear(); - PowerPC::memchecks.Clear(); - g_pCodeWindow->m_BreakpointWindow->NotifyUpdate(); - } + if (g_pCodeWindow->HasPanel<CWatchWindow>()) + g_pCodeWindow->GetPanel<CWatchWindow>()->SaveAll(); + PowerPC::watches.Clear(); + if (g_pCodeWindow->HasPanel<CBreakPointWindow>()) + g_pCodeWindow->GetPanel<CBreakPointWindow>()->SaveAll(); + PowerPC::breakpoints.Clear(); + PowerPC::memchecks.Clear(); + if (g_pCodeWindow->HasPanel<CBreakPointWindow>()) + g_pCodeWindow->GetPanel<CBreakPointWindow>()->NotifyUpdate(); g_symbolDB.Clear(); Host_NotifyMapLoaded(); } @@ -1912,7 +1880,8 @@ void CFrame::UpdateGUI() void CFrame::UpdateGameList() { - m_GameListCtrl->Update(); + if (m_GameListCtrl) + m_GameListCtrl->ReloadList(); } void CFrame::GameListChanged(wxCommandEvent& event) @@ -1987,11 +1956,7 @@ void CFrame::GameListChanged(wxCommandEvent& event) break; } - // Update gamelist - if (m_GameListCtrl) - { - m_GameListCtrl->Update(); - } + UpdateGameList(); } // Enable and disable the toolbar @@ -2047,6 +2012,6 @@ void CFrame::OnChangeColumnsVisible(wxCommandEvent& event) default: return; } - m_GameListCtrl->Update(); + UpdateGameList(); SConfig::GetInstance().SaveSettings(); } diff --git a/Source/Core/DolphinWX/GameListCtrl.cpp b/Source/Core/DolphinWX/GameListCtrl.cpp index f9ef00d910..858d273996 100644 --- a/Source/Core/DolphinWX/GameListCtrl.cpp +++ b/Source/Core/DolphinWX/GameListCtrl.cpp @@ -11,6 +11,7 @@ #include <memory> #include <string> #include <unordered_map> +#include <utility> #include <vector> #include <wx/app.h> #include <wx/bitmap.h> @@ -187,47 +188,73 @@ CGameListCtrl::~CGameListCtrl() } template <typename T> -static void InitBitmap(wxImageList* img_list, std::vector<int>* vector, T index, - const std::string& name) +static void InitBitmap(wxImageList* img_list, std::vector<int>* vector, wxWindow* context, + const wxSize& usable_size, T index, const std::string& name) { - wxSize size(96, 32); - (*vector)[static_cast<size_t>(index)] = img_list->Add(WxUtils::LoadResourceBitmap(name, size)); + wxSize size = img_list->GetSize(); + (*vector)[static_cast<size_t>(index)] = img_list->Add(WxUtils::LoadScaledResourceBitmap( + name, context, size, usable_size, WxUtils::LSI_SCALE | WxUtils::LSI_ALIGN_VCENTER)); } void CGameListCtrl::InitBitmaps() { - wxImageList* img_list = new wxImageList(96, 32); + const wxSize size = FromDIP(wxSize(96, 32)); + const wxSize flag_bmp_size = FromDIP(wxSize(32, 32)); + const wxSize platform_bmp_size = flag_bmp_size; + const wxSize rating_bmp_size = FromDIP(wxSize(48, 32)); + wxImageList* img_list = new wxImageList(size.GetWidth(), size.GetHeight()); AssignImageList(img_list, wxIMAGE_LIST_SMALL); m_FlagImageIndex.resize(static_cast<size_t>(DiscIO::Country::NUMBER_OF_COUNTRIES)); - InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_JAPAN, "Flag_Japan"); - InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_EUROPE, "Flag_Europe"); - InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_USA, "Flag_USA"); - InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_AUSTRALIA, "Flag_Australia"); - InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_FRANCE, "Flag_France"); - InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_GERMANY, "Flag_Germany"); - InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_ITALY, "Flag_Italy"); - InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_KOREA, "Flag_Korea"); - InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_NETHERLANDS, "Flag_Netherlands"); - InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_RUSSIA, "Flag_Russia"); - InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_SPAIN, "Flag_Spain"); - InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_TAIWAN, "Flag_Taiwan"); - InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_WORLD, "Flag_International"); - InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_UNKNOWN, "Flag_Unknown"); + InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_JAPAN, + "Flag_Japan"); + InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_EUROPE, + "Flag_Europe"); + InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_USA, + "Flag_USA"); + InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_AUSTRALIA, + "Flag_Australia"); + InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_FRANCE, + "Flag_France"); + InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_GERMANY, + "Flag_Germany"); + InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_ITALY, + "Flag_Italy"); + InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_KOREA, + "Flag_Korea"); + InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_NETHERLANDS, + "Flag_Netherlands"); + InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_RUSSIA, + "Flag_Russia"); + InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_SPAIN, + "Flag_Spain"); + InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_TAIWAN, + "Flag_Taiwan"); + InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_WORLD, + "Flag_International"); + InitBitmap(img_list, &m_FlagImageIndex, this, flag_bmp_size, DiscIO::Country::COUNTRY_UNKNOWN, + "Flag_Unknown"); m_PlatformImageIndex.resize(static_cast<size_t>(DiscIO::Platform::NUMBER_OF_PLATFORMS)); - InitBitmap(img_list, &m_PlatformImageIndex, DiscIO::Platform::GAMECUBE_DISC, "Platform_Gamecube"); - InitBitmap(img_list, &m_PlatformImageIndex, DiscIO::Platform::WII_DISC, "Platform_Wii"); - InitBitmap(img_list, &m_PlatformImageIndex, DiscIO::Platform::WII_WAD, "Platform_Wad"); - InitBitmap(img_list, &m_PlatformImageIndex, DiscIO::Platform::ELF_DOL, "Platform_File"); + InitBitmap(img_list, &m_PlatformImageIndex, this, platform_bmp_size, + DiscIO::Platform::GAMECUBE_DISC, "Platform_Gamecube"); + InitBitmap(img_list, &m_PlatformImageIndex, this, platform_bmp_size, DiscIO::Platform::WII_DISC, + "Platform_Wii"); + InitBitmap(img_list, &m_PlatformImageIndex, this, platform_bmp_size, DiscIO::Platform::WII_WAD, + "Platform_Wad"); + InitBitmap(img_list, &m_PlatformImageIndex, this, platform_bmp_size, DiscIO::Platform::ELF_DOL, + "Platform_File"); m_EmuStateImageIndex.resize(6); - InitBitmap(img_list, &m_EmuStateImageIndex, 0, "rating0"); - InitBitmap(img_list, &m_EmuStateImageIndex, 1, "rating1"); - InitBitmap(img_list, &m_EmuStateImageIndex, 2, "rating2"); - InitBitmap(img_list, &m_EmuStateImageIndex, 3, "rating3"); - InitBitmap(img_list, &m_EmuStateImageIndex, 4, "rating4"); - InitBitmap(img_list, &m_EmuStateImageIndex, 5, "rating5"); + InitBitmap(img_list, &m_EmuStateImageIndex, this, rating_bmp_size, 0, "rating0"); + InitBitmap(img_list, &m_EmuStateImageIndex, this, rating_bmp_size, 1, "rating1"); + InitBitmap(img_list, &m_EmuStateImageIndex, this, rating_bmp_size, 2, "rating2"); + InitBitmap(img_list, &m_EmuStateImageIndex, this, rating_bmp_size, 3, "rating3"); + InitBitmap(img_list, &m_EmuStateImageIndex, this, rating_bmp_size, 4, "rating4"); + InitBitmap(img_list, &m_EmuStateImageIndex, this, rating_bmp_size, 5, "rating5"); + + m_utility_game_banners.resize(1); + InitBitmap(img_list, &m_utility_game_banners, this, size, 0, "nobanner"); } void CGameListCtrl::BrowseForDirectory() @@ -252,11 +279,11 @@ void CGameListCtrl::BrowseForDirectory() SConfig::GetInstance().SaveSettings(); } - Update(); + ReloadList(); } } -void CGameListCtrl::Update() +void CGameListCtrl::ReloadList() { int scrollPos = wxWindow::GetScrollPos(wxVERTICAL); // Don't let the user refresh it while a game is running @@ -297,20 +324,22 @@ void CGameListCtrl::Update() // set initial sizes for columns SetColumnWidth(COLUMN_DUMMY, 0); SetColumnWidth(COLUMN_PLATFORM, SConfig::GetInstance().m_showSystemColumn ? - 32 + platform_icon_padding + platform_padding : + FromDIP(32 + platform_icon_padding + platform_padding) : 0); SetColumnWidth(COLUMN_BANNER, - SConfig::GetInstance().m_showBannerColumn ? 96 + platform_padding : 0); - SetColumnWidth(COLUMN_TITLE, 175 + platform_padding); + SConfig::GetInstance().m_showBannerColumn ? FromDIP(96 + platform_padding) : 0); + SetColumnWidth(COLUMN_TITLE, FromDIP(175 + platform_padding)); SetColumnWidth(COLUMN_MAKER, - SConfig::GetInstance().m_showMakerColumn ? 150 + platform_padding : 0); - SetColumnWidth(COLUMN_FILENAME, - SConfig::GetInstance().m_showFileNameColumn ? 100 + platform_padding : 0); - SetColumnWidth(COLUMN_ID, SConfig::GetInstance().m_showIDColumn ? 75 + platform_padding : 0); + SConfig::GetInstance().m_showMakerColumn ? FromDIP(150 + platform_padding) : 0); + SetColumnWidth(COLUMN_FILENAME, SConfig::GetInstance().m_showFileNameColumn ? + FromDIP(100 + platform_padding) : + 0); + SetColumnWidth(COLUMN_ID, + SConfig::GetInstance().m_showIDColumn ? FromDIP(75 + platform_padding) : 0); SetColumnWidth(COLUMN_COUNTRY, - SConfig::GetInstance().m_showRegionColumn ? 32 + platform_padding : 0); + SConfig::GetInstance().m_showRegionColumn ? FromDIP(32 + platform_padding) : 0); SetColumnWidth(COLUMN_EMULATION_STATE, - SConfig::GetInstance().m_showStateColumn ? 48 + platform_padding : 0); + SConfig::GetInstance().m_showStateColumn ? FromDIP(48 + platform_padding) : 0); // add all items for (int i = 0; i < (int)m_ISOFiles.size(); i++) @@ -405,10 +434,14 @@ void CGameListCtrl::UpdateItemAtColumn(long _Index, int column) } case COLUMN_BANNER: { - int ImageIndex = -1; + int ImageIndex = m_utility_game_banners[0]; // nobanner - if (rISOFile.GetBitmap().IsOk()) - ImageIndex = GetImageList(wxIMAGE_LIST_SMALL)->Add(rISOFile.GetBitmap()); + if (rISOFile.GetBannerImage().IsOk()) + { + wxImageList* img_list = GetImageList(wxIMAGE_LIST_SMALL); + ImageIndex = img_list->Add( + WxUtils::ScaleImageToBitmap(rISOFile.GetBannerImage(), this, img_list->GetSize())); + } SetItemColumnImage(_Index, COLUMN_BANNER, ImageIndex); break; @@ -502,7 +535,7 @@ void CGameListCtrl::SetBackgroundColor() void CGameListCtrl::ScanForISOs() { - ClearIsoFiles(); + m_ISOFiles.clear(); // Load custom game titles from titles.txt // http://www.gametdb.com/Wii/Downloads @@ -1098,7 +1131,7 @@ void CGameListCtrl::OnDeleteISO(wxCommandEvent& WXUNUSED(event)) { for (const GameListItem* iso : GetAllSelectedISOs()) File::Delete(iso->GetFileName()); - Update(); + ReloadList(); } } @@ -1269,7 +1302,7 @@ void CGameListCtrl::CompressSelection(bool _compress) if (!all_good) WxUtils::ShowErrorDialog(_("Dolphin was unable to complete the requested action.")); - Update(); + ReloadList(); } bool CGameListCtrl::CompressCB(const std::string& text, float percent, void* arg) @@ -1342,7 +1375,7 @@ void CGameListCtrl::OnCompressISO(wxCommandEvent& WXUNUSED(event)) if (!all_good) WxUtils::ShowErrorDialog(_("Dolphin was unable to complete the requested action.")); - Update(); + ReloadList(); } void CGameListCtrl::OnChangeDisc(wxCommandEvent& WXUNUSED(event)) diff --git a/Source/Core/DolphinWX/GameListCtrl.h b/Source/Core/DolphinWX/GameListCtrl.h index adf4a6f154..467271d68e 100644 --- a/Source/Core/DolphinWX/GameListCtrl.h +++ b/Source/Core/DolphinWX/GameListCtrl.h @@ -38,7 +38,7 @@ public: long style); ~CGameListCtrl(); - void Update() override; + void ReloadList(); void BrowseForDirectory(); const GameListItem* GetISO(size_t index) const; @@ -70,6 +70,7 @@ private: std::vector<int> m_FlagImageIndex; std::vector<int> m_PlatformImageIndex; std::vector<int> m_EmuStateImageIndex; + std::vector<int> m_utility_game_banners; std::vector<std::unique_ptr<GameListItem>> m_ISOFiles; void ClearIsoFiles() { m_ISOFiles.clear(); } diff --git a/Source/Core/DolphinWX/Globals.h b/Source/Core/DolphinWX/Globals.h index 2aa26350a6..8a303aa1ef 100644 --- a/Source/Core/DolphinWX/Globals.h +++ b/Source/Core/DolphinWX/Globals.h @@ -163,7 +163,9 @@ enum IDM_CONFIG_LOGGER, // Views - IDM_LOG_WINDOW, + // IMPORTANT: Make sure IDM_FLOAT_xxx and IDM_xxx_PARENT are kept in sync! + IDM_DEBUG_WINDOW_LIST_START, // Bookend for doing array lookups + IDM_LOG_WINDOW = IDM_DEBUG_WINDOW_LIST_START, IDM_LOG_CONFIG_WINDOW, IDM_REGISTER_WINDOW, IDM_WATCH_WINDOW, @@ -173,9 +175,10 @@ enum IDM_SOUND_WINDOW, IDM_VIDEO_WINDOW, IDM_CODE_WINDOW, + IDM_DEBUG_WINDOW_LIST_END, // Bookend for doing array lookups // List Column Title Toggles - IDM_SHOW_SYSTEM, + IDM_SHOW_SYSTEM = IDM_DEBUG_WINDOW_LIST_END, IDM_SHOW_BANNER, IDM_SHOW_MAKER, IDM_SHOW_FILENAME, @@ -345,6 +348,7 @@ enum // custom message macro #define EVT_HOST_COMMAND(id, fn) EVT_COMMAND(id, wxEVT_HOST_COMMAND, fn) +// FIXME: This should be changed to wxThreadEvent wxDECLARE_EVENT(wxEVT_HOST_COMMAND, wxCommandEvent); // Sent to wxTheApp diff --git a/Source/Core/DolphinWX/ISOFile.cpp b/Source/Core/DolphinWX/ISOFile.cpp index 1b986c02c6..05379a98a8 100644 --- a/Source/Core/DolphinWX/ISOFile.cpp +++ b/Source/Core/DolphinWX/ISOFile.cpp @@ -38,9 +38,6 @@ static const u32 CACHE_REVISION = 0x127; // Last changed in PR 3309 -#define DVD_BANNER_WIDTH 96 -#define DVD_BANNER_HEIGHT 32 - static std::string GetLanguageString(DiscIO::Language language, std::map<DiscIO::Language, std::string> strings) { @@ -165,13 +162,12 @@ GameListItem::GameListItem(const std::string& _rFileName, // Volume banner. Typical for everything that isn't a DOL or ELF. if (!m_pImage.empty()) { - wxImage image(m_ImageWidth, m_ImageHeight, &m_pImage[0], true); - m_Bitmap = ScaleBanner(&image); + // Need to make explicit copy as wxImage uses reference counting for copies combined with only + // taking a pointer, not the content, when given a buffer to its constructor. + m_image.Create(m_ImageWidth, m_ImageHeight, false); + std::memcpy(m_image.GetData(), m_pImage.data(), m_pImage.size()); return; } - - // Fallback in case no banner is available. - ReadPNGBanner(File::GetSysDirectory() + RESOURCES_DIR + DIR_SEP + "nobanner.png"); } GameListItem::~GameListItem() @@ -277,25 +273,11 @@ bool GameListItem::ReadPNGBanner(const std::string& path) return false; wxImage image(StrToWxStr(path), wxBITMAP_TYPE_PNG); - m_Bitmap = ScaleBanner(&image); - return true; -} + if (!image.IsOk()) + return false; -wxBitmap GameListItem::ScaleBanner(wxImage* image) -{ - const double gui_scale = wxTheApp->GetTopWindow()->GetContentScaleFactor(); - const double target_width = DVD_BANNER_WIDTH * gui_scale; - const double target_height = DVD_BANNER_HEIGHT * gui_scale; - const double banner_scale = - std::min(target_width / image->GetWidth(), target_height / image->GetHeight()); - image->Rescale(image->GetWidth() * banner_scale, image->GetHeight() * banner_scale, - wxIMAGE_QUALITY_HIGH); - image->Resize(wxSize(target_width, target_height), wxPoint(), 0xFF, 0xFF, 0xFF); -#ifdef __APPLE__ - return wxBitmap(*image, -1, gui_scale); -#else - return wxBitmap(*image, -1); -#endif + m_image = image; + return true; } std::string GameListItem::GetDescription(DiscIO::Language language) const diff --git a/Source/Core/DolphinWX/ISOFile.h b/Source/Core/DolphinWX/ISOFile.h index b5c19dd5ef..b8b0127e70 100644 --- a/Source/Core/DolphinWX/ISOFile.h +++ b/Source/Core/DolphinWX/ISOFile.h @@ -59,7 +59,9 @@ public: // 0 is the first disc, 1 is the second disc u8 GetDiscNumber() const { return m_disc_number; } #if defined(HAVE_WX) && HAVE_WX - const wxBitmap& GetBitmap() const { return m_Bitmap; } + // NOTE: Banner image is at the original resolution, use WxUtils::ScaleImageToBitmap + // to display it + const wxImage& GetBannerImage() const { return m_image; } #endif void DoState(PointerWrap& p); @@ -86,7 +88,7 @@ private: u16 m_Revision; #if defined(HAVE_WX) && HAVE_WX - wxBitmap m_Bitmap; + wxImage m_image; #endif bool m_Valid; std::vector<u8> m_pImage; @@ -107,6 +109,4 @@ private: void ReadVolumeBanner(const std::vector<u32>& buffer, int width, int height); // Outputs to m_Bitmap bool ReadPNGBanner(const std::string& path); - - static wxBitmap ScaleBanner(wxImage* image); }; diff --git a/Source/Core/DolphinWX/ISOProperties.cpp b/Source/Core/DolphinWX/ISOProperties.cpp index 54ca974fdb..5dcbcf591a 100644 --- a/Source/Core/DolphinWX/ISOProperties.cpp +++ b/Source/Core/DolphinWX/ISOProperties.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include <array> #include <cinttypes> #include <cstddef> #include <cstdio> @@ -33,7 +34,6 @@ #include <wx/panel.h> #include <wx/progdlg.h> #include <wx/sizer.h> -#include <wx/slider.h> #include <wx/spinctrl.h> #include <wx/statbmp.h> #include <wx/stattext.h> @@ -63,6 +63,7 @@ #include "DiscIO/VolumeCreator.h" #include "DolphinWX/Cheats/ActionReplayCodesPanel.h" #include "DolphinWX/Cheats/GeckoCodeDiag.h" +#include "DolphinWX/DolphinSlider.h" #include "DolphinWX/Frame.h" #include "DolphinWX/Globals.h" #include "DolphinWX/ISOFile.h" @@ -110,6 +111,9 @@ private: void CreateGUI() { + int space10 = FromDIP(10); + int space15 = FromDIP(15); + wxStaticBitmap* icon = new wxStaticBitmap(this, wxID_ANY, wxArtProvider::GetMessageBoxIcon(wxICON_WARNING)); m_message = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, @@ -119,10 +123,10 @@ private: m_btn_configure->Bind(wxEVT_BUTTON, &CheatWarningMessage::OnConfigureClicked, this); wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(icon, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 15); - sizer->Add(m_message, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, 15); - sizer->Add(m_btn_configure, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 10); - sizer->AddSpacer(10); + sizer->Add(icon, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space15); + sizer->Add(m_message, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, space15); + sizer->Add(m_btn_configure, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space10); + sizer->AddSpacer(space10); SetSizer(sizer); } @@ -286,8 +290,17 @@ CISOProperties::CISOProperties(const GameListItem& game_list_item, wxWindow* par bool wii = m_open_iso->GetVolumeType() != DiscIO::Platform::GAMECUBE_DISC; ChangeBannerDetails(SConfig::GetInstance().GetCurrentLanguage(wii)); - m_Banner->SetBitmap(OpenGameListItem.GetBitmap()); - m_Banner->Bind(wxEVT_RIGHT_DOWN, &CISOProperties::RightClickOnBanner, this); + if (OpenGameListItem.GetBannerImage().IsOk()) + { + m_Banner->SetBitmap(WxUtils::ScaleImageToBitmap(OpenGameListItem.GetBannerImage(), this, + m_Banner->GetMinSize())); + m_Banner->Bind(wxEVT_RIGHT_DOWN, &CISOProperties::RightClickOnBanner, this); + } + else + { + m_Banner->SetBitmap( + WxUtils::LoadScaledResourceBitmap("nobanner", this, m_Banner->GetMinSize())); + } // Filesystem browser/dumper // TODO : Should we add a way to browse the wad file ? @@ -410,6 +423,9 @@ long CISOProperties::GetElementStyle(const char* section, const char* key) void CISOProperties::CreateGUIControls() { + const int space5 = FromDIP(5); + const int space10 = FromDIP(10); + wxButton* const EditConfig = new wxButton(this, ID_EDITCONFIG, _("Edit Config")); EditConfig->SetToolTip(_("This will let you manually edit the INI config file.")); @@ -473,8 +489,8 @@ void CISOProperties::CreateGUIControls() arrayStringFor_GPUDeterminism.Add(_("fake-completion")); GPUDeterminism = new wxChoice(m_GameConfig, ID_GPUDETERMINISM, wxDefaultPosition, wxDefaultSize, arrayStringFor_GPUDeterminism); - sGPUDeterminism->Add(GPUDeterminismText); - sGPUDeterminism->Add(GPUDeterminism); + sGPUDeterminism->Add(GPUDeterminismText, 0, wxALIGN_CENTER_VERTICAL); + sGPUDeterminism->Add(GPUDeterminism, 0, wxALIGN_CENTER_VERTICAL); // Wii Console EnableWideScreen = @@ -485,7 +501,7 @@ void CISOProperties::CreateGUIControls() wxBoxSizer* const sDepthPercentage = new wxBoxSizer(wxHORIZONTAL); wxStaticText* const DepthPercentageText = new wxStaticText(m_GameConfig, wxID_ANY, _("Depth Percentage: ")); - DepthPercentage = new wxSlider(m_GameConfig, ID_DEPTHPERCENTAGE, 100, 0, 200); + DepthPercentage = new DolphinSlider(m_GameConfig, ID_DEPTHPERCENTAGE, 100, 0, 200); DepthPercentage->SetToolTip( _("This value is multiplied with the depth set in the graphics configuration.")); sDepthPercentage->Add(DepthPercentageText); @@ -518,19 +534,23 @@ void CISOProperties::CreateGUIControls() EmuState = new wxChoice(m_GameConfig, ID_EMUSTATE, wxDefaultPosition, wxDefaultSize, arrayStringFor_EmuState); EmuIssues = new wxTextCtrl(m_GameConfig, ID_EMU_ISSUES, wxEmptyString); + sEmuState->Add(EmuStateText, 0, wxALIGN_CENTER_VERTICAL); + sEmuState->Add(EmuState, 0, wxALIGN_CENTER_VERTICAL); + sEmuState->Add(EmuIssues, 1, wxEXPAND); - wxBoxSizer* const sConfigPage = new wxBoxSizer(wxVERTICAL); wxStaticBoxSizer* const sbCoreOverrides = new wxStaticBoxSizer(wxVERTICAL, m_GameConfig, _("Core")); - sbCoreOverrides->Add(CPUThread, 0, wxLEFT, 5); - sbCoreOverrides->Add(SkipIdle, 0, wxLEFT, 5); - sbCoreOverrides->Add(MMU, 0, wxLEFT, 5); - sbCoreOverrides->Add(DCBZOFF, 0, wxLEFT, 5); - sbCoreOverrides->Add(FPRF, 0, wxLEFT, 5); - sbCoreOverrides->Add(SyncGPU, 0, wxLEFT, 5); - sbCoreOverrides->Add(FastDiscSpeed, 0, wxLEFT, 5); - sbCoreOverrides->Add(DSPHLE, 0, wxLEFT, 5); - sbCoreOverrides->Add(sGPUDeterminism, 0, wxEXPAND | wxALL, 5); + sbCoreOverrides->Add(CPUThread, 0, wxLEFT | wxRIGHT, space5); + sbCoreOverrides->Add(SkipIdle, 0, wxLEFT | wxRIGHT, space5); + sbCoreOverrides->Add(MMU, 0, wxLEFT | wxRIGHT, space5); + sbCoreOverrides->Add(DCBZOFF, 0, wxLEFT | wxRIGHT, space5); + sbCoreOverrides->Add(FPRF, 0, wxLEFT | wxRIGHT, space5); + sbCoreOverrides->Add(SyncGPU, 0, wxLEFT | wxRIGHT, space5); + sbCoreOverrides->Add(FastDiscSpeed, 0, wxLEFT | wxRIGHT, space5); + sbCoreOverrides->Add(DSPHLE, 0, wxLEFT | wxRIGHT, space5); + sbCoreOverrides->AddSpacer(space5); + sbCoreOverrides->Add(sGPUDeterminism, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sbCoreOverrides->AddSpacer(space5); wxStaticBoxSizer* const sbWiiOverrides = new wxStaticBoxSizer(wxVERTICAL, m_GameConfig, _("Wii Console")); @@ -539,7 +559,7 @@ void CISOProperties::CreateGUIControls() sbWiiOverrides->ShowItems(false); EnableWideScreen->Hide(); } - sbWiiOverrides->Add(EnableWideScreen, 0, wxLEFT, 5); + sbWiiOverrides->Add(EnableWideScreen, 0, wxLEFT, space5); wxStaticBoxSizer* const sbStereoOverrides = new wxStaticBoxSizer(wxVERTICAL, m_GameConfig, _("Stereoscopy")); @@ -549,15 +569,19 @@ void CISOProperties::CreateGUIControls() wxStaticBoxSizer* const sbGameConfig = new wxStaticBoxSizer(wxVERTICAL, m_GameConfig, _("Game-Specific Settings")); - sbGameConfig->Add(OverrideText, 0, wxEXPAND | wxALL, 5); + sbGameConfig->AddSpacer(space5); + sbGameConfig->Add(OverrideText, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sbGameConfig->AddSpacer(space5); sbGameConfig->Add(sbCoreOverrides, 0, wxEXPAND); sbGameConfig->Add(sbWiiOverrides, 0, wxEXPAND); sbGameConfig->Add(sbStereoOverrides, 0, wxEXPAND); - sConfigPage->Add(sbGameConfig, 0, wxEXPAND | wxALL, 5); - sEmuState->Add(EmuStateText, 0, wxALIGN_CENTER_VERTICAL); - sEmuState->Add(EmuState, 0, wxEXPAND); - sEmuState->Add(EmuIssues, 1, wxEXPAND); - sConfigPage->Add(sEmuState, 0, wxEXPAND | wxALL, 5); + + wxBoxSizer* const sConfigPage = new wxBoxSizer(wxVERTICAL); + sConfigPage->AddSpacer(space5); + sConfigPage->Add(sbGameConfig, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sConfigPage->AddSpacer(space5); + sConfigPage->Add(sEmuState, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sConfigPage->AddSpacer(space5); m_GameConfig->SetSizer(sConfigPage); // Patches @@ -572,13 +596,15 @@ void CISOProperties::CreateGUIControls() RemovePatch->Disable(); wxBoxSizer* sPatchPage = new wxBoxSizer(wxVERTICAL); - sPatches->Add(Patches, 1, wxEXPAND | wxALL, 0); - sPatchButtons->Add(EditPatch, 0, wxEXPAND | wxALL, 0); + sPatches->Add(Patches, 1, wxEXPAND); + sPatchButtons->Add(EditPatch, 0, wxEXPAND); sPatchButtons->AddStretchSpacer(); - sPatchButtons->Add(AddPatch, 0, wxEXPAND | wxALL, 0); - sPatchButtons->Add(RemovePatch, 0, wxEXPAND | wxALL, 0); - sPatches->Add(sPatchButtons, 0, wxEXPAND | wxALL, 0); - sPatchPage->Add(sPatches, 1, wxEXPAND | wxALL, 5); + sPatchButtons->Add(AddPatch, 0, wxEXPAND); + sPatchButtons->Add(RemovePatch, 0, wxEXPAND); + sPatches->Add(sPatchButtons, 0, wxEXPAND); + sPatchPage->AddSpacer(space5); + sPatchPage->Add(sPatches, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + sPatchPage->AddSpacer(space5); m_PatchPage->SetSizer(sPatchPage); // Action Replay Cheats @@ -589,8 +615,8 @@ void CISOProperties::CreateGUIControls() m_ar_code_panel->Bind(DOLPHIN_EVT_ARCODE_TOGGLED, &CISOProperties::OnCheatCodeToggled, this); wxBoxSizer* const sCheatPage = new wxBoxSizer(wxVERTICAL); - sCheatPage->Add(m_cheats_disabled_ar, 0, wxEXPAND | wxTOP, 5); - sCheatPage->Add(m_ar_code_panel, 1, wxEXPAND | wxALL, 5); + sCheatPage->Add(m_cheats_disabled_ar, 0, wxEXPAND | wxTOP, space5); + sCheatPage->Add(m_ar_code_panel, 1, wxEXPAND | wxALL, space5); m_CheatPage->SetSizer(sCheatPage); // Gecko Cheats @@ -600,7 +626,7 @@ void CISOProperties::CreateGUIControls() m_geckocode_panel->Bind(DOLPHIN_EVT_GECKOCODE_TOGGLED, &CISOProperties::OnCheatCodeToggled, this); wxBoxSizer* gecko_layout = new wxBoxSizer(wxVERTICAL); - gecko_layout->Add(m_cheats_disabled_gecko, 0, wxEXPAND | wxTOP, 5); + gecko_layout->Add(m_cheats_disabled_gecko, 0, wxEXPAND | wxTOP, space5); gecko_layout->Add(m_geckocode_panel, 1, wxEXPAND); gecko_cheat_page->SetSizer(gecko_layout); @@ -698,67 +724,63 @@ void CISOProperties::CreateGUIControls() m_Comment = new wxTextCtrl(m_Information, ID_COMMENT, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY); wxStaticText* const m_BannerText = new wxStaticText(m_Information, wxID_ANY, _("Banner:")); - m_Banner = - new wxStaticBitmap(m_Information, ID_BANNER, wxNullBitmap, wxDefaultPosition, wxSize(96, 32)); + m_Banner = new wxStaticBitmap(m_Information, ID_BANNER, wxNullBitmap, wxDefaultPosition, + FromDIP(wxSize(96, 32))); // ISO Details - wxGridBagSizer* const sISODetails = new wxGridBagSizer(0, 0); - sISODetails->Add(m_InternalNameText, wxGBPosition(0, 0), wxGBSpan(1, 1), - wxALIGN_CENTER_VERTICAL | wxALL, 5); - sISODetails->Add(m_InternalName, wxGBPosition(0, 1), wxGBSpan(1, 2), wxEXPAND | wxALL, 5); - sISODetails->Add(m_GameIDText, wxGBPosition(1, 0), wxGBSpan(1, 1), - wxALIGN_CENTER_VERTICAL | wxALL, 5); - sISODetails->Add(m_GameID, wxGBPosition(1, 1), wxGBSpan(1, 2), wxEXPAND | wxALL, 5); - sISODetails->Add(m_CountryText, wxGBPosition(2, 0), wxGBSpan(1, 1), - wxALIGN_CENTER_VERTICAL | wxALL, 5); - sISODetails->Add(m_Country, wxGBPosition(2, 1), wxGBSpan(1, 2), wxEXPAND | wxALL, 5); - sISODetails->Add(m_MakerIDText, wxGBPosition(3, 0), wxGBSpan(1, 1), - wxALIGN_CENTER_VERTICAL | wxALL, 5); - sISODetails->Add(m_MakerID, wxGBPosition(3, 1), wxGBSpan(1, 2), wxEXPAND | wxALL, 5); - sISODetails->Add(m_RevisionText, wxGBPosition(4, 0), wxGBSpan(1, 1), - wxALIGN_CENTER_VERTICAL | wxALL, 5); - sISODetails->Add(m_Revision, wxGBPosition(4, 1), wxGBSpan(1, 2), wxEXPAND | wxALL, 5); - sISODetails->Add(m_DateText, wxGBPosition(5, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL | wxALL, - 5); - sISODetails->Add(m_Date, wxGBPosition(5, 1), wxGBSpan(1, 2), wxEXPAND | wxALL, 5); - sISODetails->Add(m_FSTText, wxGBPosition(6, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL | wxALL, - 5); - sISODetails->Add(m_FST, wxGBPosition(6, 1), wxGBSpan(1, 2), wxEXPAND | wxALL, 5); - sISODetails->Add(m_MD5SumText, wxGBPosition(7, 0), wxGBSpan(1, 1), - wxALIGN_CENTER_VERTICAL | wxALL, 5); - sISODetails->Add(m_MD5Sum, wxGBPosition(7, 1), wxGBSpan(1, 1), wxEXPAND | wxALL, 5); - wxSizer* sMD5SumButtonSizer = CreateButtonSizer(wxNO_DEFAULT); - sMD5SumButtonSizer->Add(m_MD5SumCompute); - sISODetails->Add(sMD5SumButtonSizer, wxGBPosition(7, 2), wxGBSpan(1, 1), wxEXPAND | wxALL, 5); + wxGridBagSizer* const sISODetails = new wxGridBagSizer(space10, space10); + sISODetails->Add(m_InternalNameText, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL); + sISODetails->Add(m_InternalName, wxGBPosition(0, 1), wxGBSpan(1, 2), wxEXPAND); + sISODetails->Add(m_GameIDText, wxGBPosition(1, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL); + sISODetails->Add(m_GameID, wxGBPosition(1, 1), wxGBSpan(1, 2), wxEXPAND); + sISODetails->Add(m_CountryText, wxGBPosition(2, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL); + sISODetails->Add(m_Country, wxGBPosition(2, 1), wxGBSpan(1, 2), wxEXPAND); + sISODetails->Add(m_MakerIDText, wxGBPosition(3, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL); + sISODetails->Add(m_MakerID, wxGBPosition(3, 1), wxGBSpan(1, 2), wxEXPAND); + sISODetails->Add(m_RevisionText, wxGBPosition(4, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL); + sISODetails->Add(m_Revision, wxGBPosition(4, 1), wxGBSpan(1, 2), wxEXPAND); + sISODetails->Add(m_DateText, wxGBPosition(5, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL); + sISODetails->Add(m_Date, wxGBPosition(5, 1), wxGBSpan(1, 2), wxEXPAND); + sISODetails->Add(m_FSTText, wxGBPosition(6, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL); + sISODetails->Add(m_FST, wxGBPosition(6, 1), wxGBSpan(1, 2), wxEXPAND); + sISODetails->Add(m_MD5SumText, wxGBPosition(7, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL); + sISODetails->Add(m_MD5Sum, wxGBPosition(7, 1), wxGBSpan(1, 1), wxEXPAND); + sISODetails->Add(m_MD5SumCompute, wxGBPosition(7, 2), wxGBSpan(1, 1), wxEXPAND); sISODetails->AddGrowableCol(1); wxStaticBoxSizer* const sbISODetails = new wxStaticBoxSizer(wxVERTICAL, m_Information, _("ISO Details")); - sbISODetails->Add(sISODetails, 0, wxEXPAND, 5); + sbISODetails->AddSpacer(space5); + sbISODetails->Add(sISODetails, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sbISODetails->AddSpacer(space5); // Banner Details - wxGridBagSizer* const sBannerDetails = new wxGridBagSizer(0, 0); - sBannerDetails->Add(m_LangText, wxGBPosition(0, 0), wxGBSpan(1, 1), - wxALIGN_CENTER_VERTICAL | wxALL, 5); - sBannerDetails->Add(m_Lang, wxGBPosition(0, 1), wxGBSpan(1, 1), wxEXPAND | wxALL, 5); - sBannerDetails->Add(m_NameText, wxGBPosition(1, 0), wxGBSpan(1, 1), - wxALIGN_CENTER_VERTICAL | wxALL, 5); - sBannerDetails->Add(m_Name, wxGBPosition(1, 1), wxGBSpan(1, 1), wxEXPAND | wxALL, 5); - sBannerDetails->Add(m_MakerText, wxGBPosition(2, 0), wxGBSpan(1, 1), - wxALIGN_CENTER_VERTICAL | wxALL, 5); - sBannerDetails->Add(m_Maker, wxGBPosition(2, 1), wxGBSpan(1, 1), wxEXPAND | wxALL, 5); - sBannerDetails->Add(m_CommentText, wxGBPosition(3, 0), wxGBSpan(1, 1), wxALL, 5); - sBannerDetails->Add(m_Comment, wxGBPosition(3, 1), wxGBSpan(1, 1), wxEXPAND | wxALL, 5); - sBannerDetails->Add(m_BannerText, wxGBPosition(4, 0), wxGBSpan(1, 1), wxALL, 5); - sBannerDetails->Add(m_Banner, wxGBPosition(4, 1), wxGBSpan(1, 1), wxEXPAND | wxALL, 5); + wxGridBagSizer* const sBannerDetails = new wxGridBagSizer(space10, space10); + sBannerDetails->Add(m_LangText, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL); + // Comboboxes cannot be safely stretched vertically on Windows. + sBannerDetails->Add(WxUtils::GiveMinSize(m_Lang, wxDefaultSize), wxGBPosition(0, 1), + wxGBSpan(1, 1), wxEXPAND); + sBannerDetails->Add(m_NameText, wxGBPosition(1, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL); + sBannerDetails->Add(m_Name, wxGBPosition(1, 1), wxGBSpan(1, 1), wxEXPAND); + sBannerDetails->Add(m_MakerText, wxGBPosition(2, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL); + sBannerDetails->Add(m_Maker, wxGBPosition(2, 1), wxGBSpan(1, 1), wxEXPAND); + sBannerDetails->Add(m_CommentText, wxGBPosition(3, 0), wxGBSpan(1, 1)); + sBannerDetails->Add(m_Comment, wxGBPosition(3, 1), wxGBSpan(1, 1), wxEXPAND); + sBannerDetails->Add(m_BannerText, wxGBPosition(4, 0), wxGBSpan(1, 1)); + sBannerDetails->Add(m_Banner, wxGBPosition(4, 1), wxGBSpan(1, 1), wxEXPAND); sBannerDetails->AddGrowableCol(1); wxStaticBoxSizer* const sbBannerDetails = new wxStaticBoxSizer(wxVERTICAL, m_Information, _("Banner Details")); - sbBannerDetails->Add(sBannerDetails, 0, wxEXPAND, 5); + sbBannerDetails->AddSpacer(space5); + sbBannerDetails->Add(sBannerDetails, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sbBannerDetails->AddSpacer(space5); wxBoxSizer* const sInfoPage = new wxBoxSizer(wxVERTICAL); - sInfoPage->Add(sbISODetails, 0, wxEXPAND | wxALL, 5); - sInfoPage->Add(sbBannerDetails, 0, wxEXPAND | wxALL, 5); + sInfoPage->AddSpacer(space5); + sInfoPage->Add(sbISODetails, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sInfoPage->AddSpacer(space5); + sInfoPage->Add(sbBannerDetails, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sInfoPage->AddSpacer(space5); m_Information->SetSizer(sInfoPage); if (m_open_iso->GetVolumeType() != DiscIO::Platform::WII_WAD) @@ -767,10 +789,14 @@ void CISOProperties::CreateGUIControls() m_Notebook->AddPage(filesystem_panel, _("Filesystem")); // Filesystem icons - wxImageList* const m_iconList = new wxImageList(16, 16); - m_iconList->Add(WxUtils::LoadResourceBitmap("isoproperties_disc")); // 0 - m_iconList->Add(WxUtils::LoadResourceBitmap("isoproperties_folder")); // 1 - m_iconList->Add(WxUtils::LoadResourceBitmap("isoproperties_file")); // 2 + wxSize icon_size = FromDIP(wxSize(16, 16)); + wxImageList* const m_iconList = new wxImageList(icon_size.GetWidth(), icon_size.GetHeight()); + static const std::array<const char* const, 3> s_icon_names{ + {"isoproperties_disc", "isoproperties_folder", "isoproperties_file"}}; + for (const auto& name : s_icon_names) + m_iconList->Add( + WxUtils::LoadScaledResourceBitmap(name, this, icon_size, wxDefaultSize, + WxUtils::LSI_SCALE_DOWN | WxUtils::LSI_ALIGN_CENTER)); // Filesystem tree m_Treectrl = new wxTreeCtrl(filesystem_panel, ID_TREECTRL); @@ -778,14 +804,16 @@ void CISOProperties::CreateGUIControls() RootId = m_Treectrl->AddRoot(_("Disc"), 0, 0, nullptr); wxBoxSizer* sTreePage = new wxBoxSizer(wxVERTICAL); - sTreePage->Add(m_Treectrl, 1, wxEXPAND | wxALL, 5); + sTreePage->AddSpacer(space5); + sTreePage->Add(m_Treectrl, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + sTreePage->AddSpacer(space5); filesystem_panel->SetSizer(sTreePage); } - wxSizer* sButtons = CreateButtonSizer(wxNO_DEFAULT); + wxStdDialogButtonSizer* sButtons = CreateStdDialogButtonSizer(wxOK | wxNO_DEFAULT); sButtons->Prepend(EditConfigDefault); sButtons->Prepend(EditConfig); - sButtons->Add(new wxButton(this, wxID_OK, _("Close"))); + sButtons->GetAffirmativeButton()->SetLabel(_("Close")); // If there is no default gameini, disable the button. bool game_ini_exists = false; @@ -803,10 +831,15 @@ void CISOProperties::CreateGUIControls() // Add notebook and buttons to the dialog wxBoxSizer* sMain = new wxBoxSizer(wxVERTICAL); - sMain->Add(m_Notebook, 1, wxEXPAND | wxALL, 5); - sMain->Add(sButtons, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); - sMain->SetMinSize(wxSize(500, -1)); + sMain->AddSpacer(space5); + sMain->Add(m_Notebook, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + sMain->AddSpacer(space5); + sMain->Add(sButtons, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sMain->AddSpacer(space5); + sMain->SetMinSize(FromDIP(wxSize(500, -1))); + SetLayoutAdaptationMode(wxDIALOG_ADAPTATION_MODE_ENABLED); + SetLayoutAdaptationLevel(wxDIALOG_ADAPTATION_STANDARD_SIZER); SetSizerAndFit(sMain); Center(); SetFocus(); @@ -842,7 +875,7 @@ void CISOProperties::OnBannerImageSave(wxCommandEvent& WXUNUSED(event)) wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (dialog.ShowModal() == wxID_OK) { - m_Banner->GetBitmap().ConvertToImage().SaveFile(dialog.GetPath()); + OpenGameListItem.GetBannerImage().SaveFile(dialog.GetPath()); } Raise(); } diff --git a/Source/Core/DolphinWX/ISOProperties.h b/Source/Core/DolphinWX/ISOProperties.h index c3e70e62dd..922b188dde 100644 --- a/Source/Core/DolphinWX/ISOProperties.h +++ b/Source/Core/DolphinWX/ISOProperties.h @@ -19,11 +19,11 @@ #include "DolphinWX/ISOFile.h" #include "DolphinWX/PatchAddEdit.h" +class DolphinSlider; class wxButton; class wxCheckBox; class wxCheckListBox; class wxChoice; -class wxSlider; class wxSpinCtrl; class wxStaticBitmap; class wxTextCtrl; @@ -90,7 +90,7 @@ private: wxCheckBox* EnableWideScreen; // Stereoscopy - wxSlider* DepthPercentage; + DolphinSlider* DepthPercentage; wxSpinCtrl* Convergence; wxCheckBox* MonoDepth; diff --git a/Source/Core/DolphinWX/InputConfigDiag.cpp b/Source/Core/DolphinWX/InputConfigDiag.cpp index 1be1a2436c..7978226711 100644 --- a/Source/Core/DolphinWX/InputConfigDiag.cpp +++ b/Source/Core/DolphinWX/InputConfigDiag.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <algorithm> +#include <array> #include <cctype> #include <cstddef> #include <memory> @@ -16,6 +17,7 @@ #include <wx/choice.h> #include <wx/combobox.h> #include <wx/control.h> +#include <wx/dcclient.h> #include <wx/dcmemory.h> #include <wx/dialog.h> #include <wx/event.h> @@ -25,9 +27,9 @@ #include <wx/notebook.h> #include <wx/panel.h> #include <wx/sizer.h> -#include <wx/slider.h> #include <wx/spinctrl.h> #include <wx/statbmp.h> +#include <wx/statbox.h> #include <wx/stattext.h> #include <wx/textctrl.h> #include <wx/timer.h> @@ -41,6 +43,7 @@ #include "Core/HW/GCPad.h" #include "Core/HW/Wiimote.h" #include "Core/HotkeyManager.h" +#include "DolphinWX/DolphinSlider.h" #include "DolphinWX/InputConfigDiag.h" #include "DolphinWX/WxUtils.h" #include "InputCommon/ControllerEmu.h" @@ -66,8 +69,10 @@ void GamepadPage::ConfigExtension(wxCommandEvent& event) ControlGroupsSizer* const szr = new ControlGroupsSizer( ex->attachments[ex->switch_extension].get(), &dlg, this, &control_groups); - main_szr->Add(szr, 0, wxLEFT, 5); - main_szr->Add(dlg.CreateButtonSizer(wxOK), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + const int space5 = FromDIP(5); + main_szr->Add(szr, 0, wxLEFT, space5); + main_szr->Add(dlg.CreateButtonSizer(wxOK), 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_szr->AddSpacer(space5); dlg.SetSizerAndFit(main_szr); dlg.Center(); @@ -122,6 +127,19 @@ void PadSettingCheckBox::UpdateValue() setting->SetValue(((wxCheckBox*)wxcontrol)->GetValue()); } +PadSettingSpin::PadSettingSpin(wxWindow* const parent, + ControllerEmu::ControlGroup::NumericSetting* const settings) + : PadSetting(new wxSpinCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, + wxSP_ARROW_KEYS, settings->m_low, settings->m_high, + (int)(settings->m_value * 100))), + setting(settings) +{ + // Compute how wide the control needs to be to fit the maximum value in it. + // This accounts for borders, margins and the spinner buttons. + wxcontrol->SetMinSize(WxUtils::GetTextWidgetMinSize(static_cast<wxSpinCtrl*>(wxcontrol))); + wxcontrol->SetLabelText(setting->m_name); +} + void PadSettingSpin::UpdateGUI() { ((wxSpinCtrl*)wxcontrol)->SetValue((int)(setting->GetValue() * 100)); @@ -139,13 +157,12 @@ ControlDialog::ControlDialog(GamepadPage* const parent, InputConfig& config, control_reference(ref), m_config(config), m_parent(parent) { m_devq = m_parent->controller->default_device; + const int space5 = FromDIP(5); // GetStrings() sounds slow :/ - // device_cbox = new wxComboBox(this, wxID_ANY, StrToWxStr(ref->device_qualifier.ToString()), - // wxDefaultPosition, wxSize(256,-1), parent->device_cbox->GetStrings(), wxTE_PROCESS_ENTER); - device_cbox = - new wxComboBox(this, wxID_ANY, StrToWxStr(m_devq.ToString()), wxDefaultPosition, - wxSize(256, -1), parent->device_cbox->GetStrings(), wxTE_PROCESS_ENTER); + device_cbox = new wxComboBox(this, wxID_ANY, StrToWxStr(m_devq.ToString()), wxDefaultPosition, + wxDLG_UNIT(this, wxSize(180, -1)), parent->device_cbox->GetStrings(), + wxTE_PROCESS_ENTER); device_cbox->Bind(wxEVT_COMBOBOX, &ControlDialog::SetDevice, this); device_cbox->Bind(wxEVT_TEXT_ENTER, &ControlDialog::SetDevice, this); @@ -153,12 +170,19 @@ ControlDialog::ControlDialog(GamepadPage* const parent, InputConfig& config, wxStaticBoxSizer* const control_chooser = CreateControlChooser(parent); wxStaticBoxSizer* const d_szr = new wxStaticBoxSizer(wxVERTICAL, this, _("Device")); - d_szr->Add(device_cbox, 0, wxEXPAND | wxALL, 5); + d_szr->AddSpacer(space5); + d_szr->Add(device_cbox, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + d_szr->AddSpacer(space5); wxBoxSizer* const szr = new wxBoxSizer(wxVERTICAL); - szr->Add(d_szr, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 5); - szr->Add(control_chooser, 1, wxEXPAND | wxALL, 5); + szr->AddSpacer(space5); + szr->Add(d_szr, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + szr->AddSpacer(space5); + szr->Add(control_chooser, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + szr->AddSpacer(space5); + SetLayoutAdaptationMode(wxDIALOG_ADAPTATION_MODE_ENABLED); + SetLayoutAdaptationLevel(wxDIALOG_ADAPTATION_STANDARD_SIZER); SetSizerAndFit(szr); // needed UpdateGUI(); @@ -169,15 +193,26 @@ ControlButton::ControlButton(wxWindow* const parent, ControllerInterface::ControlReference* const _ref, const std::string& name, const unsigned int width, const std::string& label) - : wxButton(parent, wxID_ANY, "", wxDefaultPosition, wxSize(width, 20)), control_reference(_ref), - m_name(name) + : wxButton(parent, wxID_ANY), control_reference(_ref), m_name(name), + m_configured_width(FromDIP(width)) { if (label.empty()) - SetLabel(StrToWxStr(_ref->expression)); + SetLabelText(StrToWxStr(_ref->expression)); else SetLabel(StrToWxStr(label)); } +wxSize ControlButton::DoGetBestSize() const +{ + if (m_configured_width == wxDefaultCoord) + return wxButton::DoGetBestSize(); + + static constexpr int PADDING_HEIGHT = 4; + wxClientDC dc(const_cast<ControlButton*>(this)); + wxFontMetrics metrics = dc.GetFontMetrics(); + return {m_configured_width, metrics.height + FromDIP(PADDING_HEIGHT * 2)}; +} + void InputConfigDialog::UpdateProfileComboBox() { std::string pname(File::GetUserPath(D_CONFIG_IDX)); @@ -222,7 +257,7 @@ void InputConfigDialog::OnCloseButton(wxCommandEvent& event) int ControlDialog::GetRangeSliderValue() const { - return range_slider->GetValue(); + return m_range_slider->GetValue(); } void ControlDialog::UpdateListContents() @@ -288,9 +323,7 @@ void GamepadPage::UpdateGUI() { for (ControlButton* button : cgBox->control_buttons) { - wxString expr = StrToWxStr(button->control_reference->expression); - expr.Replace("&", "&&"); - button->SetLabel(expr); + button->SetLabelText(StrToWxStr(button->control_reference->expression)); } for (PadSetting* padSetting : cgBox->options) @@ -503,6 +536,23 @@ void GamepadPage::EnableControlButton(const std::string& group_name, const std:: (*it)->Enable(enabled); } +void ControlDialog::OnRangeSlide(wxScrollEvent& event) +{ + m_range_spinner->SetValue(event.GetPosition()); + control_reference->range = static_cast<ControlState>(event.GetPosition()) / SLIDER_TICK_COUNT; +} + +void ControlDialog::OnRangeSpin(wxSpinEvent& event) +{ + m_range_slider->SetValue(event.GetValue()); + control_reference->range = static_cast<ControlState>(event.GetValue()) / SLIDER_TICK_COUNT; +} + +void ControlDialog::OnRangeThumbtrack(wxScrollEvent& event) +{ + m_range_spinner->SetValue(event.GetPosition()); +} + void GamepadPage::AdjustSetting(wxCommandEvent& event) { const auto* const control = static_cast<wxControl*>(event.GetEventObject()); @@ -528,12 +578,6 @@ void GamepadPage::AdjustBooleanSetting(wxCommandEvent& event) } } -void GamepadPage::AdjustControlOption(wxCommandEvent&) -{ - m_control_dialog->control_reference->range = - (ControlState)(m_control_dialog->GetRangeSliderValue()) / SLIDER_TICK_COUNT; -} - void GamepadPage::ConfigControl(wxEvent& event) { m_control_dialog = new ControlDialog(this, m_config, @@ -647,9 +691,10 @@ wxStaticBoxSizer* ControlDialog::CreateControlChooser(GamepadPage* const parent) { wxStaticBoxSizer* const main_szr = new wxStaticBoxSizer( wxVERTICAL, this, control_reference->is_input ? _("Input") : _("Output")); + const int space5 = FromDIP(5); - textctrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1, 48), - wxTE_MULTILINE | wxTE_RICH2); + textctrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, + wxDLG_UNIT(this, wxSize(-1, 32)), wxTE_MULTILINE | wxTE_RICH2); wxFont font = textctrl->GetFont(); font.SetFamily(wxFONTFAMILY_MODERN); textctrl->SetFont(font); @@ -665,12 +710,12 @@ wxStaticBoxSizer* ControlDialog::CreateControlChooser(GamepadPage* const parent) wxButton* const or_button = new wxButton(this, wxID_ANY, _("| OR")); or_button->Bind(wxEVT_BUTTON, &ControlDialog::AppendControl, this); - control_lbox = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 64)); + control_lbox = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDLG_UNIT(this, wxSize(-1, 48))); wxBoxSizer* const button_sizer = new wxBoxSizer(wxVERTICAL); - button_sizer->Add(detect_button, 1, 0, 5); - button_sizer->Add(select_button, 1, 0, 5); - button_sizer->Add(or_button, 1, 0, 5); + button_sizer->Add(detect_button, 1); + button_sizer->Add(select_button, 1); + button_sizer->Add(or_button, 1); if (control_reference->is_input) { @@ -683,43 +728,57 @@ wxStaticBoxSizer* ControlDialog::CreateControlChooser(GamepadPage* const parent) not_button->Bind(wxEVT_BUTTON, &ControlDialog::AppendControl, this); add_button->Bind(wxEVT_BUTTON, &ControlDialog::AppendControl, this); - button_sizer->Add(and_button, 1, 0, 5); - button_sizer->Add(not_button, 1, 0, 5); - button_sizer->Add(add_button, 1, 0, 5); + button_sizer->Add(and_button, 1); + button_sizer->Add(not_button, 1); + button_sizer->Add(add_button, 1); } - range_slider = - new wxSlider(this, wxID_ANY, SLIDER_TICK_COUNT, -SLIDER_TICK_COUNT * 5, SLIDER_TICK_COUNT * 5, - wxDefaultPosition, wxDefaultSize, wxSL_TOP | wxSL_LABELS /*| wxSL_AUTOTICKS*/); - - range_slider->SetValue((int)(control_reference->range * SLIDER_TICK_COUNT)); + m_range_slider = new DolphinSlider( + this, wxID_ANY, static_cast<int>(control_reference->range * SLIDER_TICK_COUNT), + -SLIDER_TICK_COUNT * 5, SLIDER_TICK_COUNT * 5, wxDefaultPosition, wxDefaultSize, wxSL_TOP); + m_range_spinner = new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, + wxDLG_UNIT(this, wxSize(32, -1)), + wxSP_ARROW_KEYS | wxALIGN_RIGHT, m_range_slider->GetMin(), + m_range_slider->GetMax(), m_range_slider->GetValue()); detect_button->Bind(wxEVT_BUTTON, &ControlDialog::DetectControl, this); clear_button->Bind(wxEVT_BUTTON, &ControlDialog::ClearControl, this); - range_slider->Bind(wxEVT_SCROLL_CHANGED, &GamepadPage::AdjustControlOption, parent); - wxStaticText* const range_label = new wxStaticText(this, wxID_ANY, _("Range")); + m_range_slider->Bind(wxEVT_SCROLL_CHANGED, &ControlDialog::OnRangeSlide, this); + m_range_slider->Bind(wxEVT_SCROLL_THUMBTRACK, &ControlDialog::OnRangeThumbtrack, this); + m_range_spinner->Bind(wxEVT_SPINCTRL, &ControlDialog::OnRangeSpin, this); m_bound_label = new wxStaticText(this, wxID_ANY, ""); m_error_label = new wxStaticText(this, wxID_ANY, ""); wxBoxSizer* const range_sizer = new wxBoxSizer(wxHORIZONTAL); - range_sizer->Add(range_label, 0, wxCENTER | wxLEFT, 5); - range_sizer->Add(range_slider, 1, wxEXPAND | wxLEFT, 5); + range_sizer->Add(new wxStaticText(this, wxID_ANY, _("Range")), 0, wxALIGN_CENTER_VERTICAL); + range_sizer->Add( + new wxStaticText(this, wxID_ANY, wxString::Format("%d", m_range_slider->GetMin())), 0, + wxALIGN_CENTER_VERTICAL | wxLEFT, space5); + range_sizer->Add(m_range_slider, 1, wxEXPAND); + range_sizer->Add( + new wxStaticText(this, wxID_ANY, wxString::Format("%d", m_range_slider->GetMax())), 0, + wxALIGN_CENTER_VERTICAL); + range_sizer->Add(m_range_spinner, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); wxBoxSizer* const ctrls_sizer = new wxBoxSizer(wxHORIZONTAL); - ctrls_sizer->Add(control_lbox, 1, wxEXPAND, 0); - ctrls_sizer->Add(button_sizer, 0, wxEXPAND, 0); + ctrls_sizer->Add(control_lbox, 1, wxEXPAND); + ctrls_sizer->Add(button_sizer, 0, wxEXPAND); wxSizer* const bottom_btns_sizer = CreateButtonSizer(wxOK | wxAPPLY); - bottom_btns_sizer->Prepend(clear_button, 0, wxLEFT, 5); + bottom_btns_sizer->Prepend(clear_button, 0, wxLEFT, space5); - main_szr->Add(range_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, 5); - main_szr->Add(ctrls_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); - main_szr->Add(textctrl, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); - main_szr->Add(bottom_btns_sizer, 0, wxEXPAND | wxBOTTOM | wxRIGHT, 5); - main_szr->Add(m_bound_label, 0, wxCENTER, 0); - main_szr->Add(m_error_label, 0, wxCENTER, 0); + main_szr->Add(range_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_szr->AddSpacer(space5); + main_szr->Add(ctrls_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_szr->AddSpacer(space5); + main_szr->Add(textctrl, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_szr->AddSpacer(space5); + main_szr->Add(bottom_btns_sizer, 0, wxEXPAND | wxRIGHT, space5); + main_szr->AddSpacer(space5); + main_szr->Add(m_bound_label, 0, wxALIGN_CENTER_HORIZONTAL); + main_szr->Add(m_error_label, 0, wxALIGN_CENTER_HORIZONTAL); UpdateListContents(); @@ -839,22 +898,25 @@ ControlGroupBox::~ControlGroupBox() ControlGroupBox::ControlGroupBox(ControllerEmu::ControlGroup* const group, wxWindow* const parent, GamepadPage* const eventsink) - : wxBoxSizer(wxVERTICAL), control_group(group) + : wxBoxSizer(wxVERTICAL), control_group(group), static_bitmap(nullptr), m_scale(1) { - static_bitmap = nullptr; - const std::vector<std::string> exclude_buttons = {"Mic", "Modifier"}; - const std::vector<std::string> exclude_groups = {"IR", "Swing", "Tilt", "Shake", - "UDP Wiimote", "Extension", "Rumble"}; + static constexpr std::array<const char* const, 2> exclude_buttons{{"Mic", "Modifier"}}; + static constexpr std::array<const char* const, 7> exclude_groups{ + {"IR", "Swing", "Tilt", "Shake", "UDP Wiimote", "Extension", "Rumble"}}; - wxFont m_SmallFont(7, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL); - for (auto& control : group->controls) + wxFont small_font(7, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL); + const int space3 = parent->FromDIP(3); + + wxFlexGridSizer* control_grid = new wxFlexGridSizer(2, 0, space3); + control_grid->AddGrowableCol(0); + for (const auto& control : group->controls) { wxStaticText* const label = new wxStaticText(parent, wxID_ANY, wxGetTranslation(StrToWxStr(control->name))); ControlButton* const control_button = new ControlButton(parent, control->control_ref.get(), control->name, 80); - control_button->SetFont(m_SmallFont); + control_button->SetFont(small_font); control_buttons.push_back(control_button); if (std::find(exclude_groups.begin(), exclude_groups.end(), control_group->name) == @@ -878,15 +940,10 @@ ControlGroupBox::ControlGroupBox(ControllerEmu::ControlGroup* const group, wxWin control_button->Bind(wxEVT_MIDDLE_DOWN, &GamepadPage::ClearControl, eventsink); control_button->Bind(wxEVT_RIGHT_UP, &GamepadPage::ConfigControl, eventsink); - wxBoxSizer* const control_sizer = new wxBoxSizer(wxHORIZONTAL); - control_sizer->AddStretchSpacer(1); - control_sizer->Add(label, 0, wxCENTER | wxRIGHT, 3); - control_sizer->Add(control_button, 0, 0, 0); - - Add(control_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, 3); + control_grid->Add(label, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT); + control_grid->Add(control_button, 0, wxALIGN_CENTER_VERTICAL); } - - wxMemoryDC dc; + Add(control_grid, 0, wxEXPAND | wxLEFT | wxRIGHT, space3); switch (group->type) { @@ -895,9 +952,15 @@ ControlGroupBox::ControlGroupBox(ControllerEmu::ControlGroup* const group, wxWin case GROUP_TYPE_CURSOR: case GROUP_TYPE_FORCE: { - wxBitmap bitmap(64, 64); - dc.SelectObject(bitmap); + wxSize bitmap_size = parent->FromDIP(wxSize(64, 64)); + m_scale = bitmap_size.GetWidth() / 64.0; + + wxBitmap bitmap; + bitmap.CreateScaled(bitmap_size.GetWidth(), bitmap_size.GetHeight(), wxBITMAP_SCREEN_DEPTH, + parent->GetContentScaleFactor()); + wxMemoryDC dc(bitmap); dc.Clear(); + dc.SelectObject(wxNullBitmap); static_bitmap = new wxStaticBitmap(parent, wxID_ANY, bitmap, wxDefaultPosition, wxDefaultSize, wxBITMAP_TYPE_BMP); @@ -909,21 +972,23 @@ ControlGroupBox::ControlGroupBox(ControllerEmu::ControlGroup* const group, wxWin options.push_back(setting); szr->Add( new wxStaticText(parent, wxID_ANY, wxGetTranslation(StrToWxStr(groupSetting->m_name)))); - szr->Add(setting->wxcontrol, 0, wxLEFT, 0); + szr->Add(setting->wxcontrol); } for (auto& groupSetting : group->boolean_settings) { auto* checkbox = new PadSettingCheckBox(parent, groupSetting.get()); checkbox->wxcontrol->Bind(wxEVT_CHECKBOX, &GamepadPage::AdjustBooleanSetting, eventsink); options.push_back(checkbox); - Add(checkbox->wxcontrol, 0, wxALL | wxLEFT, 5); + Add(checkbox->wxcontrol, 0, wxALL | wxLEFT, space3); } wxBoxSizer* const h_szr = new wxBoxSizer(wxHORIZONTAL); - h_szr->Add(szr, 1, 0, 5); - h_szr->Add(static_bitmap, 0, wxALL | wxCENTER, 3); + h_szr->Add(szr, 1, wxEXPAND | wxTOP | wxBOTTOM, space3); + h_szr->AddSpacer(space3); + h_szr->Add(static_bitmap, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, space3); - Add(h_szr, 0, wxEXPAND | wxLEFT | wxCENTER | wxTOP, 3); + AddSpacer(space3); + Add(h_szr, 0, wxEXPAND | wxLEFT | wxRIGHT, space3); } break; case GROUP_TYPE_BUTTONS: @@ -931,10 +996,16 @@ ControlGroupBox::ControlGroupBox(ControllerEmu::ControlGroup* const group, wxWin // Draw buttons in rows of 8 unsigned int button_cols = group->controls.size() > 8 ? 8 : group->controls.size(); unsigned int button_rows = ceil((float)group->controls.size() / 8.0f); - wxBitmap bitmap(int(12 * button_cols + 1), (11 * button_rows) + 1); + wxSize bitmap_size(12 * button_cols + 1, 11 * button_rows + 1); + wxSize bitmap_scaled_size = parent->FromDIP(bitmap_size); + m_scale = static_cast<double>(bitmap_scaled_size.GetWidth()) / bitmap_size.GetWidth(); - dc.SelectObject(bitmap); + wxBitmap bitmap; + bitmap.CreateScaled(bitmap_scaled_size.GetWidth(), bitmap_scaled_size.GetHeight(), + wxBITMAP_SCREEN_DEPTH, parent->GetContentScaleFactor()); + wxMemoryDC dc(bitmap); dc.Clear(); + dc.SelectObject(wxNullBitmap); static_bitmap = new wxStaticBitmap(parent, wxID_ANY, bitmap, wxDefaultPosition, wxDefaultSize, wxBITMAP_TYPE_BMP); @@ -949,11 +1020,13 @@ ControlGroupBox::ControlGroupBox(ControllerEmu::ControlGroup* const group, wxWin wxBoxSizer* const szr = new wxBoxSizer(wxHORIZONTAL); szr->Add(new wxStaticText(parent, wxID_ANY, wxGetTranslation(StrToWxStr(group->numeric_settings[0]->m_name))), - 0, wxCENTER | wxRIGHT, 3); - szr->Add(threshold_cbox->wxcontrol, 0, wxRIGHT, 3); + 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, space3); + szr->Add(threshold_cbox->wxcontrol, 0, wxRIGHT, space3); - Add(szr, 0, wxALL | wxCENTER, 3); - Add(static_bitmap, 0, wxALL | wxCENTER, 3); + AddSpacer(space3); + Add(szr, 0, wxALIGN_CENTER_HORIZONTAL | wxLEFT | wxRIGHT, space3); + AddSpacer(space3); + Add(static_bitmap, 0, wxALIGN_CENTER_HORIZONTAL | wxLEFT | wxRIGHT, space3); } break; case GROUP_TYPE_MIXED_TRIGGERS: @@ -968,10 +1041,16 @@ ControlGroupBox::ControlGroupBox(ControllerEmu::ControlGroup* const group, wxWin if (GROUP_TYPE_TRIGGERS != group->type) height /= 2; + height += 1; - wxBitmap bitmap(width, height + 1); - dc.SelectObject(bitmap); + wxSize bitmap_size = parent->FromDIP(wxSize(width, height)); + m_scale = static_cast<double>(bitmap_size.GetWidth()) / width; + wxBitmap bitmap; + bitmap.CreateScaled(bitmap_size.GetWidth(), bitmap_size.GetHeight(), wxBITMAP_SCREEN_DEPTH, + parent->GetContentScaleFactor()); + wxMemoryDC dc(bitmap); dc.Clear(); + dc.SelectObject(wxNullBitmap); static_bitmap = new wxStaticBitmap(parent, wxID_ANY, bitmap, wxDefaultPosition, wxDefaultSize, wxBITMAP_TYPE_BMP); @@ -983,12 +1062,15 @@ ControlGroupBox::ControlGroupBox(ControllerEmu::ControlGroup* const group, wxWin wxBoxSizer* const szr = new wxBoxSizer(wxHORIZONTAL); szr->Add( new wxStaticText(parent, wxID_ANY, wxGetTranslation(StrToWxStr(groupSetting->m_name))), 0, - wxCENTER | wxRIGHT, 3); - szr->Add(setting->wxcontrol, 0, wxRIGHT, 3); - Add(szr, 0, wxALL | wxCENTER, 3); + wxALIGN_CENTER_VERTICAL); + szr->Add(setting->wxcontrol, 0, wxLEFT, space3); + + AddSpacer(space3); + Add(szr, 0, wxALIGN_CENTER_HORIZONTAL | wxLEFT | wxRIGHT, space3); } - Add(static_bitmap, 0, wxALL | wxCENTER, 3); + AddSpacer(space3); + Add(static_bitmap, 0, wxALIGN_CENTER_HORIZONTAL | wxLEFT | wxRIGHT, space3); } break; case GROUP_TYPE_EXTENSION: @@ -1002,8 +1084,10 @@ ControlGroupBox::ControlGroupBox(ControllerEmu::ControlGroup* const group, wxWin attachments->wxcontrol->Bind(wxEVT_CHOICE, &GamepadPage::AdjustSetting, eventsink); configure_btn->Bind(wxEVT_BUTTON, &GamepadPage::ConfigExtension, eventsink); - Add(attachments->wxcontrol, 0, wxTOP | wxLEFT | wxRIGHT | wxEXPAND, 3); - Add(configure_btn, 0, wxALL | wxEXPAND, 3); + AddSpacer(space3); + Add(attachments->wxcontrol, 0, wxEXPAND | wxLEFT | wxRIGHT, space3); + AddSpacer(space3); + Add(configure_btn, 0, wxEXPAND | wxLEFT | wxRIGHT, space3); } break; default: @@ -1016,7 +1100,8 @@ ControlGroupBox::ControlGroupBox(ControllerEmu::ControlGroup* const group, wxWin if (groupSetting->m_name == "Iterative Input") groupSetting->SetValue(false); options.push_back(setting_cbox); - Add(setting_cbox->wxcontrol, 0, wxALL | wxLEFT, 5); + AddSpacer(space3); + Add(setting_cbox->wxcontrol, 0, wxLEFT | wxRIGHT, space3); } for (auto& groupSetting : group->numeric_settings) { @@ -1026,17 +1111,15 @@ ControlGroupBox::ControlGroupBox(ControllerEmu::ControlGroup* const group, wxWin wxBoxSizer* const szr = new wxBoxSizer(wxHORIZONTAL); szr->Add( new wxStaticText(parent, wxID_ANY, wxGetTranslation(StrToWxStr(groupSetting->m_name))), 0, - wxCENTER | wxRIGHT, 3); - szr->Add(setting->wxcontrol, 0, wxRIGHT, 3); - Add(szr, 0, wxALL | wxCENTER, 3); + wxALIGN_CENTER_VERTICAL, space3); + szr->Add(setting->wxcontrol, 0, wxLEFT, space3); + AddSpacer(space3); + Add(szr, 0, wxLEFT | wxRIGHT, space3); } + break; } - break; } - - dc.SelectObject(wxNullBitmap); - - // AddStretchSpacer(0); + AddSpacer(space3); } ControlGroupsSizer::ControlGroupsSizer(ControllerEmu* const controller, wxWindow* const parent, @@ -1044,15 +1127,17 @@ ControlGroupsSizer::ControlGroupsSizer(ControllerEmu* const controller, wxWindow std::vector<ControlGroupBox*>* groups) : wxBoxSizer(wxHORIZONTAL) { + const int space5 = parent->FromDIP(5); size_t col_size = 0; wxBoxSizer* stacked_groups = nullptr; for (auto& group : controller->groups) { - ControlGroupBox* control_group_box = new ControlGroupBox(group.get(), parent, eventsink); wxStaticBoxSizer* control_group = new wxStaticBoxSizer(wxVERTICAL, parent, wxGetTranslation(StrToWxStr(group->ui_name))); - control_group->Add(control_group_box); + ControlGroupBox* control_group_box = + new ControlGroupBox(group.get(), control_group->GetStaticBox(), eventsink); + control_group->Add(control_group_box, 0, wxEXPAND); const size_t grp_size = group->controls.size() + group->numeric_settings.size() + group->boolean_settings.size(); @@ -1060,7 +1145,10 @@ ControlGroupsSizer::ControlGroupsSizer(ControllerEmu* const controller, wxWindow if (col_size > 8 || nullptr == stacked_groups) { if (stacked_groups) - Add(stacked_groups, 0, /*wxEXPAND|*/ wxBOTTOM | wxRIGHT, 5); + { + Add(stacked_groups, 0, wxBOTTOM, space5); + AddSpacer(space5); + } stacked_groups = new wxBoxSizer(wxVERTICAL); stacked_groups->Add(control_group, 0, wxEXPAND); @@ -1077,7 +1165,7 @@ ControlGroupsSizer::ControlGroupsSizer(ControllerEmu* const controller, wxWindow } if (stacked_groups) - Add(stacked_groups, 0, /*wxEXPAND|*/ wxBOTTOM | wxRIGHT, 5); + Add(stacked_groups, 0, wxBOTTOM, space5); } GamepadPage::GamepadPage(wxWindow* parent, InputConfig& config, const int pad_num, @@ -1086,65 +1174,81 @@ GamepadPage::GamepadPage(wxWindow* parent, InputConfig& config, const int pad_nu m_config_dialog(config_dialog), m_config(config) { wxBoxSizer* control_group_sizer = new ControlGroupsSizer(controller, this, this, &control_groups); - - wxStaticBoxSizer* profile_sbox = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Profile")); + const int space3 = FromDIP(3); + const int space5 = FromDIP(5); // device chooser + wxStaticBoxSizer* const device_sbox = new wxStaticBoxSizer(wxVERTICAL, this, _("Device")); - wxStaticBoxSizer* const device_sbox = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Device")); - - device_cbox = new wxComboBox(this, wxID_ANY, "", wxDefaultPosition, wxSize(64, -1)); + device_cbox = new wxComboBox(device_sbox->GetStaticBox(), wxID_ANY, ""); device_cbox->ToggleWindowStyle(wxTE_PROCESS_ENTER); - wxButton* refresh_button = - new wxButton(this, wxID_ANY, _("Refresh"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + wxButton* refresh_button = new wxButton(device_sbox->GetStaticBox(), wxID_ANY, _("Refresh"), + wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); device_cbox->Bind(wxEVT_COMBOBOX, &GamepadPage::SetDevice, this); device_cbox->Bind(wxEVT_TEXT_ENTER, &GamepadPage::SetDevice, this); refresh_button->Bind(wxEVT_BUTTON, &GamepadPage::RefreshDevices, this); - device_sbox->Add(device_cbox, 1, wxLEFT | wxRIGHT, 3); - device_sbox->Add(refresh_button, 0, wxRIGHT | wxBOTTOM, 3); + wxBoxSizer* const device_sbox_in = new wxBoxSizer(wxHORIZONTAL); + device_sbox_in->Add(WxUtils::GiveMinSizeDIP(device_cbox, wxSize(64, -1)), 1, + wxALIGN_CENTER_VERTICAL); + device_sbox_in->Add(refresh_button, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space3); + device_sbox->Add(device_sbox_in, 1, wxEXPAND | wxLEFT | wxRIGHT, space3); + device_sbox->AddSpacer(space3); - wxButton* const default_button = - new wxButton(this, wxID_ANY, _("Default"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - wxButton* const clearall_button = - new wxButton(this, wxID_ANY, _("Clear"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + // Clear / Reset buttons + wxStaticBoxSizer* const clear_sbox = new wxStaticBoxSizer(wxVERTICAL, this, _("Reset")); - wxStaticBoxSizer* const clear_sbox = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Reset")); - clear_sbox->Add(default_button, 1, wxLEFT, 3); - clear_sbox->Add(clearall_button, 1, wxRIGHT, 3); + wxButton* const default_button = new wxButton(clear_sbox->GetStaticBox(), wxID_ANY, _("Default"), + wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + wxButton* const clearall_button = new wxButton(clear_sbox->GetStaticBox(), wxID_ANY, _("Clear"), + wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + + wxBoxSizer* clear_sbox_in = new wxBoxSizer(wxHORIZONTAL); + clear_sbox_in->Add(default_button, 1, wxALIGN_CENTER_VERTICAL); + clear_sbox_in->Add(clearall_button, 1, wxALIGN_CENTER_VERTICAL); + clear_sbox->Add(clear_sbox_in, 1, wxEXPAND | wxLEFT | wxRIGHT, space3); + clear_sbox->AddSpacer(space3); clearall_button->Bind(wxEVT_BUTTON, &GamepadPage::ClearAll, this); default_button->Bind(wxEVT_BUTTON, &GamepadPage::LoadDefaults, this); - profile_cbox = new wxComboBox(this, wxID_ANY, "", wxDefaultPosition, wxSize(64, -1)); + // Profile selector + wxStaticBoxSizer* profile_sbox = new wxStaticBoxSizer(wxVERTICAL, this, _("Profile")); + profile_cbox = new wxComboBox(profile_sbox->GetStaticBox(), wxID_ANY, ""); - wxButton* const pload_btn = - new wxButton(this, wxID_ANY, _("Load"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - wxButton* const psave_btn = - new wxButton(this, wxID_ANY, _("Save"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - wxButton* const pdelete_btn = - new wxButton(this, wxID_ANY, _("Delete"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + wxButton* const pload_btn = new wxButton(profile_sbox->GetStaticBox(), wxID_ANY, _("Load"), + wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + wxButton* const psave_btn = new wxButton(profile_sbox->GetStaticBox(), wxID_ANY, _("Save"), + wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + wxButton* const pdelete_btn = new wxButton(profile_sbox->GetStaticBox(), wxID_ANY, _("Delete"), + wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); pload_btn->Bind(wxEVT_BUTTON, &GamepadPage::LoadProfile, this); psave_btn->Bind(wxEVT_BUTTON, &GamepadPage::SaveProfile, this); pdelete_btn->Bind(wxEVT_BUTTON, &GamepadPage::DeleteProfile, this); - profile_sbox->Add(profile_cbox, 1, wxLEFT, 3); - profile_sbox->Add(pload_btn, 0, wxLEFT, 3); - profile_sbox->Add(psave_btn, 0, 0, 3); - profile_sbox->Add(pdelete_btn, 0, wxRIGHT | wxBOTTOM, 3); + wxBoxSizer* profile_sbox_in = new wxBoxSizer(wxHORIZONTAL); + profile_sbox_in->Add(WxUtils::GiveMinSizeDIP(profile_cbox, wxSize(64, -1)), 1, + wxALIGN_CENTER_VERTICAL); + profile_sbox_in->AddSpacer(space3); + profile_sbox_in->Add(pload_btn, 0, wxALIGN_CENTER_VERTICAL); + profile_sbox_in->Add(psave_btn, 0, wxALIGN_CENTER_VERTICAL); + profile_sbox_in->Add(pdelete_btn, 0, wxALIGN_CENTER_VERTICAL); + profile_sbox->Add(profile_sbox_in, 1, wxEXPAND | wxLEFT | wxRIGHT, space3); + profile_sbox->AddSpacer(space3); wxBoxSizer* const dio = new wxBoxSizer(wxHORIZONTAL); - dio->Add(device_sbox, 1, wxEXPAND | wxRIGHT, 5); - dio->Add(clear_sbox, 0, wxEXPAND | wxRIGHT, 5); - dio->Add(profile_sbox, 1, wxEXPAND | wxRIGHT, 5); + dio->Add(device_sbox, 1, wxEXPAND); + dio->Add(clear_sbox, 0, wxEXPAND | wxLEFT, space5); + dio->Add(profile_sbox, 1, wxEXPAND | wxLEFT, space5); wxBoxSizer* const mapping = new wxBoxSizer(wxVERTICAL); - - mapping->Add(dio, 1, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 5); - mapping->Add(control_group_sizer, 0, wxLEFT | wxEXPAND, 5); + mapping->AddSpacer(space5); + mapping->Add(dio, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + mapping->AddSpacer(space5); + mapping->Add(control_group_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); UpdateGUI(); @@ -1154,9 +1258,9 @@ GamepadPage::GamepadPage(wxWindow* parent, InputConfig& config, const int pad_nu InputConfigDialog::InputConfigDialog(wxWindow* const parent, InputConfig& config, const wxString& name, const int tab_num) - : wxDialog(parent, wxID_ANY, name, wxPoint(128, -1)), m_config(config) + : wxDialog(parent, wxID_ANY, name), m_config(config) { - m_pad_notebook = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_DEFAULT); + m_pad_notebook = new wxNotebook(this, wxID_ANY); GamepadPage* gp = new GamepadPage(m_pad_notebook, m_config, tab_num, this); m_padpages.push_back(gp); m_pad_notebook->AddPage(gp, wxString::Format("%s [%u]", @@ -1172,10 +1276,15 @@ InputConfigDialog::InputConfigDialog(wxWindow* const parent, InputConfig& config Bind(wxEVT_BUTTON, &InputConfigDialog::OnCloseButton, this, wxID_CLOSE); wxBoxSizer* const szr = new wxBoxSizer(wxVERTICAL); - szr->Add(m_pad_notebook, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5); - szr->Add(CreateButtonSizer(wxCLOSE), 0, wxEXPAND | wxALL, 5); + const int space5 = FromDIP(5); + szr->AddSpacer(space5); + szr->Add(m_pad_notebook, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + szr->AddSpacer(space5); + szr->Add(CreateButtonSizer(wxCLOSE | wxNO_DEFAULT), 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + szr->AddSpacer(space5); SetLayoutAdaptationMode(wxDIALOG_ADAPTATION_MODE_ENABLED); + SetLayoutAdaptationLevel(wxDIALOG_ADAPTATION_STANDARD_SIZER); SetSizerAndFit(szr); Center(); diff --git a/Source/Core/DolphinWX/InputConfigDiag.h b/Source/Core/DolphinWX/InputConfigDiag.h index 41390d8d60..210a01d312 100644 --- a/Source/Core/DolphinWX/InputConfigDiag.h +++ b/Source/Core/DolphinWX/InputConfigDiag.h @@ -27,11 +27,11 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" #include "InputCommon/ControllerInterface/Device.h" +class DolphinSlider; class InputConfig; class wxComboBox; class wxListBox; class wxNotebook; -class wxSlider; class wxStaticBitmap; class wxStaticText; class wxTextCtrl; @@ -62,15 +62,7 @@ class PadSettingSpin : public PadSetting { public: PadSettingSpin(wxWindow* const parent, - ControllerEmu::ControlGroup::NumericSetting* const _setting) - : PadSetting(new wxSpinCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, - wxSize(54, -1), 0, _setting->m_low, _setting->m_high, - (int)(_setting->GetValue() * 100))), - setting(_setting) - { - wxcontrol->SetLabel(setting->m_name); - } - + ControllerEmu::ControlGroup::NumericSetting* const setting); void UpdateGUI() override; void UpdateValue() override; @@ -136,6 +128,9 @@ private: void SetSelectedControl(wxCommandEvent& event); void AppendControl(wxCommandEvent& event); + void OnRangeSlide(wxScrollEvent&); + void OnRangeSpin(wxSpinEvent&); + void OnRangeThumbtrack(wxScrollEvent&); bool GetExpressionForSelectedControl(wxString& expr); @@ -143,7 +138,8 @@ private: wxComboBox* device_cbox; wxTextCtrl* textctrl; wxListBox* control_lbox; - wxSlider* range_slider; + DolphinSlider* m_range_slider; + wxSpinCtrl* m_range_spinner; wxStaticText* m_bound_label; wxStaticText* m_error_label; InputEventFilter m_event_filter; @@ -165,10 +161,15 @@ class ControlButton : public wxButton { public: ControlButton(wxWindow* const parent, ControllerInterface::ControlReference* const _ref, - const std::string& name, const unsigned int width, const std::string& label = ""); + const std::string& name, const unsigned int width, const std::string& label = {}); ControllerInterface::ControlReference* const control_reference; const std::string m_name; + +protected: + wxSize DoGetBestSize() const override; + + int m_configured_width = wxDefaultCoord; }; class ControlGroupBox : public wxBoxSizer @@ -178,11 +179,18 @@ public: GamepadPage* const eventsink); ~ControlGroupBox(); + bool HasBitmapHeading() const + { + return control_group->type == GROUP_TYPE_STICK || control_group->type == GROUP_TYPE_TILT || + control_group->type == GROUP_TYPE_CURSOR || control_group->type == GROUP_TYPE_FORCE; + } + std::vector<PadSetting*> options; ControllerEmu::ControlGroup* const control_group; wxStaticBitmap* static_bitmap; std::vector<ControlButton*> control_buttons; + double m_scale; }; class ControlGroupsSizer : public wxBoxSizer diff --git a/Source/Core/DolphinWX/InputConfigDiagBitmaps.cpp b/Source/Core/DolphinWX/InputConfigDiagBitmaps.cpp index 8f972c9091..fa4f10ff13 100644 --- a/Source/Core/DolphinWX/InputConfigDiagBitmaps.cpp +++ b/Source/Core/DolphinWX/InputConfigDiagBitmaps.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include <algorithm> +#include <cmath> #include <cstring> #include <memory> #include <string> @@ -11,8 +13,10 @@ #include <wx/colour.h> #include <wx/dcmemory.h> #include <wx/font.h> +#include <wx/graphics.h> #include <wx/notebook.h> #include <wx/pen.h> +#include <wx/settings.h> #include <wx/statbmp.h> #include "DolphinWX/InputConfigDiag.h" @@ -36,69 +40,64 @@ struct ShapePosition }; // regular octagon -static void DrawOctagon(wxDC* dc, ShapePosition p) +static void DrawOctagon(wxGraphicsContext* gc, ShapePosition p) { - const int vertices = 8; + static constexpr int vertices = 8; double radius = p.max; - wxPoint point[vertices]; + wxGraphicsPath path = gc->CreatePath(); double angle = 2.0 * M_PI / vertices; for (int i = 0; i < vertices; i++) { - double a = (angle * i); + double a = angle * i; double x = radius * cos(a); double y = radius * sin(a); - point[i].x = x; - point[i].y = y; + if (i == 0) + path.MoveToPoint(x, y); + else + path.AddLineToPoint(x, y); } + path.CloseSubpath(); - dc->DrawPolygon(vertices, point, p.offset, p.offset); + wxGraphicsMatrix matrix = gc->CreateMatrix(); + matrix.Translate(p.offset, p.offset); + path.Transform(matrix); + + gc->DrawPath(path); } // irregular dodecagon -static void DrawDodecagon(wxDC* dc, ShapePosition p) +static void DrawDodecagon(wxGraphicsContext* gc, ShapePosition p) { - const int vertices = 12; + wxGraphicsPath path = gc->CreatePath(); + path.MoveToPoint(p.dz, p.max); + path.AddLineToPoint(p.diag, p.diag); + path.AddLineToPoint(p.max, p.dz); + path.AddLineToPoint(p.max, -p.dz); + path.AddLineToPoint(p.diag, -p.diag); + path.AddLineToPoint(p.dz, -p.max); + path.AddLineToPoint(-p.dz, -p.max); + path.AddLineToPoint(-p.diag, -p.diag); + path.AddLineToPoint(-p.max, -p.dz); + path.AddLineToPoint(-p.max, p.dz); + path.AddLineToPoint(-p.diag, p.diag); + path.AddLineToPoint(-p.dz, p.max); + path.CloseSubpath(); - wxPoint point[vertices]; - point[0].x = p.dz; - point[0].y = p.max; - point[1].x = p.diag; - point[1].y = p.diag; - point[2].x = p.max; - point[2].y = p.dz; + wxGraphicsMatrix matrix = gc->CreateMatrix(); + matrix.Translate(p.offset, p.offset); + path.Transform(matrix); - point[3].x = p.max; - point[3].y = -p.dz; - point[4].x = p.diag; - point[4].y = -p.diag; - point[5].x = p.dz; - point[5].y = -p.max; - - point[6].x = -p.dz; - point[6].y = -p.max; - point[7].x = -p.diag; - point[7].y = -p.diag; - point[8].x = -p.max; - point[8].y = -p.dz; - - point[9].x = -p.max; - point[9].y = p.dz; - point[10].x = -p.diag; - point[10].y = p.diag; - point[11].x = -p.dz; - point[11].y = p.max; - - dc->DrawPolygon(vertices, point, p.offset, p.offset); + gc->DrawPath(path); } -static void DrawCenteredRectangle(wxDC& dc, int x, int y, int w, int h) +static void DrawCenteredRectangle(wxGraphicsContext* gc, double x, double y, double w, double h) { x -= w / 2; y -= h / 2; - dc.DrawRectangle(x, y, w, h); + gc->DrawRectangle(x, y, w, h); } #define VIS_BITMAP_SIZE 64 @@ -108,37 +107,47 @@ static void DrawCenteredRectangle(wxDC& dc, int x, int y, int w, int h) #define COORD_VIS_SIZE 4 -static void DrawCoordinate(wxDC& dc, ControlState x, ControlState y) +static void DrawCoordinate(wxGraphicsContext* gc, ControlState x, ControlState y) { - int xc = VIS_COORD(x); - int yc = VIS_COORD(y); - DrawCenteredRectangle(dc, xc, yc, COORD_VIS_SIZE, COORD_VIS_SIZE); + double xc = VIS_COORD(x); + double yc = VIS_COORD(y); + DrawCenteredRectangle(gc, xc, yc, COORD_VIS_SIZE, COORD_VIS_SIZE); } -static void DrawButton(unsigned int* const bitmasks, unsigned int buttons, unsigned int n, wxDC& dc, - ControlGroupBox* g, unsigned int row) +static void DrawButton(const std::vector<unsigned int>& bitmasks, unsigned int buttons, + unsigned int n, wxGraphicsContext* gc, ControlGroupBox* g, unsigned int row, + const wxGraphicsMatrix& null_matrix) { if (buttons & bitmasks[(row * 8) + n]) { - dc.SetBrush(*wxRED_BRUSH); + gc->SetBrush(*wxRED_BRUSH); } else { - auto lock = ControllerEmu::GetStateLock(); unsigned char amt = 255 - g->control_group->controls[(row * 8) + n]->control_ref->State() * 128; - dc.SetBrush(wxBrush(wxColour(amt, amt, amt))); + gc->SetBrush(wxBrush(wxColour(amt, amt, amt))); } - dc.DrawRectangle(n * 12, (row == 0) ? 0 : (row * 11), 14, 12); + gc->DrawRectangle(n * 12, (row == 0) ? 0 : (row * 11), 14, 12); // text const std::string name = g->control_group->controls[(row * 8) + n]->name; + // Matrix transformation needs to be disabled so we don't draw scaled/zoomed text. + wxGraphicsMatrix old_matrix = gc->GetTransform(); + gc->SetTransform(null_matrix); // bit of hax so ZL, ZR show up as L, R - dc.DrawText(StrToWxStr(std::string(1, (name[1] && name[1] < 'a') ? name[1] : name[0])), - n * 12 + 2, 1 + ((row == 0) ? 0 : (row * 11))); + gc->DrawText(wxUniChar((name[1] && name[1] < 'a') ? name[1] : name[0]), (n * 12 + 2) * g->m_scale, + (1 + (row == 0 ? 0 : row * 11)) * g->m_scale); + gc->SetTransform(old_matrix); } -static void DrawControlGroupBox(wxDC& dc, ControlGroupBox* g) +static void DrawControlGroupBox(wxGraphicsContext* gc, ControlGroupBox* g) { + wxGraphicsMatrix null_matrix = gc->GetTransform(); + wxGraphicsMatrix scale_matrix = null_matrix; + scale_matrix.Scale(g->m_scale, g->m_scale); + + gc->SetTransform(scale_matrix); + switch (g->control_group->type) { case GROUP_TYPE_TILT: @@ -165,29 +174,17 @@ static void DrawControlGroupBox(wxDC& dc, ControlGroupBox* g) // ir cursor forward movement if (GROUP_TYPE_CURSOR == g->control_group->type) { - if (z) - { - dc.SetPen(*wxRED_PEN); - dc.SetBrush(*wxRED_BRUSH); - } - else - { - dc.SetPen(*wxGREY_PEN); - dc.SetBrush(*wxGREY_BRUSH); - } - dc.DrawRectangle(0, 31 - z * 31, 64, 2); + gc->SetBrush(z ? *wxRED_BRUSH : *wxGREY_BRUSH); + wxGraphicsPath path = gc->CreatePath(); + path.AddRectangle(0, 31 - z * 31, 64, 2); + gc->FillPath(path); } // input zone - dc.SetPen(*wxLIGHT_GREY_PEN); - dc.SetBrush(*wxWHITE_BRUSH); + gc->SetPen(*wxLIGHT_GREY_PEN); if (GROUP_TYPE_STICK == g->control_group->type) { - // outline and fill colors - wxBrush LightGrayBrush("#dddddd"); - wxPen LightGrayPen("#bfbfbf"); - dc.SetBrush(LightGrayBrush); - dc.SetPen(LightGrayPen); + gc->SetBrush(wxColour(0xDDDDDD)); // Light Gray ShapePosition p; p.box = 64; @@ -215,20 +212,25 @@ static void DrawControlGroupBox(wxDC& dc, ControlGroupBox* g) } if (octagon) - DrawOctagon(&dc, p); + DrawOctagon(gc, p); else - DrawDodecagon(&dc, p); + DrawDodecagon(gc, p); } else { - dc.DrawRectangle(16, 16, 32, 32); + gc->SetBrush(*wxWHITE_BRUSH); + gc->DrawRectangle(16, 16, 32, 32); } if (GROUP_TYPE_CURSOR != g->control_group->type) { - // deadzone circle - dc.SetBrush(*wxLIGHT_GREY_BRUSH); - dc.DrawCircle(32, 32, g->control_group->numeric_settings[SETTING_DEADZONE]->GetValue() * 32); + int deadzone_idx = g->control_group->type == GROUP_TYPE_STICK ? SETTING_DEADZONE : 0; + wxGraphicsPath path = gc->CreatePath(); + path.AddCircle(VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE / 2, + g->control_group->numeric_settings[deadzone_idx]->GetValue() * + VIS_BITMAP_SIZE / 2); + gc->SetBrush(*wxLIGHT_GREY_BRUSH); + gc->FillPath(path); } // raw dot @@ -238,21 +240,21 @@ static void DrawControlGroupBox(wxDC& dc, ControlGroupBox* g) yy = g->control_group->controls[1]->control_ref->State(); yy -= g->control_group->controls[0]->control_ref->State(); - dc.SetPen(*wxGREY_PEN); - dc.SetBrush(*wxGREY_BRUSH); - DrawCoordinate(dc, xx, yy); + gc->SetPen(*wxTRANSPARENT_PEN); + gc->SetBrush(*wxGREY_BRUSH); + DrawCoordinate(gc, xx, yy); // adjusted dot if (x != 0 || y != 0) { - dc.SetPen(*wxRED_PEN); - dc.SetBrush(*wxRED_BRUSH); + gc->SetBrush(*wxRED_BRUSH); // XXX: The adjusted values flip the Y axis to be in the format // the Wii expects. Should this be in WiimoteEmu.cpp instead? - DrawCoordinate(dc, x, -y); + DrawCoordinate(gc, x, -y); } } break; + case GROUP_TYPE_FORCE: { ControlState raw_dot[3]; @@ -270,69 +272,67 @@ static void DrawControlGroupBox(wxDC& dc, ControlGroupBox* g) } // deadzone rect for forward/backward visual - dc.SetBrush(*wxLIGHT_GREY_BRUSH); - dc.SetPen(*wxLIGHT_GREY_PEN); + gc->SetPen(*wxTRANSPARENT_PEN); + gc->SetBrush(*wxLIGHT_GREY_BRUSH); int deadzone_height = deadzone * VIS_BITMAP_SIZE; - DrawCenteredRectangle(dc, 0, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE, deadzone_height); + DrawCenteredRectangle(gc, 0, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE, deadzone_height); #define LINE_HEIGHT 2 int line_y; // raw forward/background line - dc.SetPen(*wxGREY_PEN); - dc.SetBrush(*wxGREY_BRUSH); + gc->SetBrush(*wxGREY_BRUSH); line_y = VIS_COORD(raw_dot[2]); - DrawCenteredRectangle(dc, VIS_BITMAP_SIZE / 2, line_y, VIS_BITMAP_SIZE, LINE_HEIGHT); + DrawCenteredRectangle(gc, VIS_BITMAP_SIZE / 2, line_y, VIS_BITMAP_SIZE, LINE_HEIGHT); // adjusted forward/background line if (adj_dot[2] != 0.0) { - dc.SetPen(*wxRED_PEN); - dc.SetBrush(*wxRED_BRUSH); + gc->SetBrush(*wxRED_BRUSH); line_y = VIS_COORD(adj_dot[2]); - DrawCenteredRectangle(dc, VIS_BITMAP_SIZE / 2, line_y, VIS_BITMAP_SIZE, LINE_HEIGHT); + DrawCenteredRectangle(gc, VIS_BITMAP_SIZE / 2, line_y, VIS_BITMAP_SIZE, LINE_HEIGHT); } #define DEADZONE_RECT_SIZE 32 // empty deadzone square - dc.SetBrush(*wxWHITE_BRUSH); - dc.SetPen(*wxLIGHT_GREY_PEN); - DrawCenteredRectangle(dc, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE / 2, DEADZONE_RECT_SIZE, + gc->SetPen(*wxLIGHT_GREY_PEN); + gc->SetBrush(*wxWHITE_BRUSH); + DrawCenteredRectangle(gc, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE / 2, DEADZONE_RECT_SIZE, DEADZONE_RECT_SIZE); // deadzone square - dc.SetBrush(*wxLIGHT_GREY_BRUSH); + gc->SetPen(*wxTRANSPARENT_PEN); + gc->SetBrush(*wxLIGHT_GREY_BRUSH); int dz_size = (deadzone * DEADZONE_RECT_SIZE); - DrawCenteredRectangle(dc, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE / 2, dz_size, dz_size); + DrawCenteredRectangle(gc, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE / 2, dz_size, dz_size); // raw dot - dc.SetPen(*wxGREY_PEN); - dc.SetBrush(*wxGREY_BRUSH); - DrawCoordinate(dc, raw_dot[1], raw_dot[0]); + gc->SetBrush(*wxGREY_BRUSH); + DrawCoordinate(gc, raw_dot[1], raw_dot[0]); // adjusted dot if (adj_dot[1] != 0 && adj_dot[0] != 0) { - dc.SetPen(*wxRED_PEN); - dc.SetBrush(*wxRED_BRUSH); - DrawCoordinate(dc, adj_dot[1], adj_dot[0]); + gc->SetBrush(*wxRED_BRUSH); + DrawCoordinate(gc, adj_dot[1], adj_dot[0]); } } break; + case GROUP_TYPE_BUTTONS: { - unsigned int button_count = ((unsigned int)g->control_group->controls.size()); + const unsigned int button_count = static_cast<unsigned int>(g->control_group->controls.size()); + std::vector<unsigned int> bitmasks(button_count); // draw the shit - dc.SetPen(*wxGREY_PEN); + gc->SetPen(*wxGREY_PEN); - unsigned int* const bitmasks = new unsigned int[button_count]; for (unsigned int n = 0; n < button_count; ++n) bitmasks[n] = (1 << n); unsigned int buttons = 0; - ((ControllerEmu::Buttons*)g->control_group)->GetState(&buttons, bitmasks); + ((ControllerEmu::Buttons*)g->control_group)->GetState(&buttons, bitmasks.data()); // Draw buttons in rows of 8 for (unsigned int row = 0; row < ceil((float)button_count / 8.0f); row++) @@ -343,155 +343,200 @@ static void DrawControlGroupBox(wxDC& dc, ControlGroupBox* g) for (unsigned int n = 0; n < buttons_to_draw; ++n) { - DrawButton(bitmasks, buttons, n, dc, g, row); + DrawButton(bitmasks, buttons, n, gc, g, row, null_matrix); } } - - delete[] bitmasks; } break; + case GROUP_TYPE_TRIGGERS: { - const unsigned int trigger_count = ((unsigned int)(g->control_group->controls.size())); + const unsigned int trigger_count = static_cast<unsigned int>(g->control_group->controls.size()); + std::vector<ControlState> trigs(trigger_count); // draw the shit - dc.SetPen(*wxGREY_PEN); - ControlState deadzone = g->control_group->numeric_settings[0]->GetValue(); + gc->SetPen(*wxGREY_PEN); - ControlState* const trigs = new ControlState[trigger_count]; - ((ControllerEmu::Triggers*)g->control_group)->GetState(trigs); + ControlState deadzone = g->control_group->numeric_settings[0]->GetValue(); + ((ControllerEmu::Triggers*)g->control_group)->GetState(trigs.data()); for (unsigned int n = 0; n < trigger_count; ++n) { ControlState trig_r = g->control_group->controls[n]->control_ref->State(); // outline - dc.SetPen(*wxGREY_PEN); - dc.SetBrush(*wxWHITE_BRUSH); - dc.DrawRectangle(0, n * 12, 64, 14); + gc->SetBrush(*wxWHITE_BRUSH); + gc->DrawRectangle(0, n * 12, 64, 14); // raw - dc.SetBrush(*wxGREY_BRUSH); - dc.DrawRectangle(0, n * 12, trig_r * 64, 14); + gc->SetBrush(*wxGREY_BRUSH); + gc->DrawRectangle(0, n * 12, trig_r * 64, 14); // deadzone affected - dc.SetBrush(*wxRED_BRUSH); - dc.DrawRectangle(0, n * 12, trigs[n] * 64, 14); + gc->SetBrush(*wxRED_BRUSH); + gc->DrawRectangle(0, n * 12, trigs[n] * 64, 14); // text - dc.DrawText(StrToWxStr(g->control_group->controls[n]->name), 3, n * 12 + 1); + // We don't want the text to be scaled/zoomed + gc->SetTransform(null_matrix); + gc->DrawText(StrToWxStr(g->control_group->controls[n]->name), 3 * g->m_scale, + (n * 12 + 1) * g->m_scale); + gc->SetTransform(scale_matrix); } - delete[] trigs; - // deadzone box - dc.SetPen(*wxLIGHT_GREY_PEN); - dc.SetBrush(*wxTRANSPARENT_BRUSH); - dc.DrawRectangle(0, 0, deadzone * 64, trigger_count * 14); + gc->SetPen(*wxLIGHT_GREY_PEN); + gc->SetBrush(*wxTRANSPARENT_BRUSH); + gc->DrawRectangle(0, 0, deadzone * 64, trigger_count * 14); } break; + case GROUP_TYPE_MIXED_TRIGGERS: { const unsigned int trigger_count = ((unsigned int)(g->control_group->controls.size() / 2)); // draw the shit - dc.SetPen(*wxGREY_PEN); + gc->SetPen(*wxGREY_PEN); ControlState thresh = g->control_group->numeric_settings[0]->GetValue(); for (unsigned int n = 0; n < trigger_count; ++n) { - dc.SetBrush(*wxRED_BRUSH); + gc->SetBrush(*wxRED_BRUSH); ControlState trig_d = g->control_group->controls[n]->control_ref->State(); ControlState trig_a = trig_d > thresh ? 1 : g->control_group->controls[n + trigger_count]->control_ref->State(); - dc.DrawRectangle(0, n * 12, 64 + 20, 14); + gc->DrawRectangle(0, n * 12, 64 + 20, 14); if (trig_d <= thresh) - dc.SetBrush(*wxWHITE_BRUSH); - dc.DrawRectangle(trig_a * 64, n * 12, 64 + 20, 14); - dc.DrawRectangle(64, n * 12, 32, 14); + gc->SetBrush(*wxWHITE_BRUSH); + gc->DrawRectangle(trig_a * 64, n * 12, 64 + 20, 14); + gc->DrawRectangle(64, n * 12, 32, 14); // text - dc.DrawText(StrToWxStr(g->control_group->controls[n + trigger_count]->name), 3, n * 12 + 1); - dc.DrawText(StrToWxStr(std::string(1, g->control_group->controls[n]->name[0])), 64 + 3, - n * 12 + 1); + // We don't want the text to be scaled/zoomed + gc->SetTransform(null_matrix); + gc->DrawText(StrToWxStr(g->control_group->controls[n + trigger_count]->name), 3 * g->m_scale, + (n * 12 + 1) * g->m_scale); + gc->DrawText(StrToWxStr(std::string(1, g->control_group->controls[n]->name[0])), + (64 + 3) * g->m_scale, (n * 12 + 1) * g->m_scale); + gc->SetTransform(scale_matrix); } // threshold box - dc.SetPen(*wxLIGHT_GREY_PEN); - dc.SetBrush(*wxTRANSPARENT_BRUSH); - dc.DrawRectangle(thresh * 64, 0, 128, trigger_count * 14); + gc->SetPen(*wxLIGHT_GREY_PEN); + gc->SetBrush(*wxTRANSPARENT_BRUSH); + gc->DrawRectangle(thresh * 64, 0, 128, trigger_count * 14); } break; + case GROUP_TYPE_SLIDER: { const ControlState deadzone = g->control_group->numeric_settings[0]->GetValue(); ControlState state = g->control_group->controls[1]->control_ref->State() - g->control_group->controls[0]->control_ref->State(); - dc.SetPen(*wxGREY_PEN); - dc.SetBrush(*wxGREY_BRUSH); - dc.DrawRectangle(31 + state * 30, 0, 2, 14); + gc->SetPen(*wxTRANSPARENT_PEN); + gc->SetBrush(*wxGREY_BRUSH); + gc->DrawRectangle(31 + state * 30, 0, 2, 14); ControlState adj_state; ((ControllerEmu::Slider*)g->control_group)->GetState(&adj_state); if (state) { - dc.SetPen(*wxRED_PEN); - dc.SetBrush(*wxRED_BRUSH); - dc.DrawRectangle(31 + adj_state * 30, 0, 2, 14); + gc->SetBrush(*wxRED_BRUSH); + gc->DrawRectangle(31 + adj_state * 30, 0, 2, 14); } // deadzone box - dc.SetPen(*wxLIGHT_GREY_PEN); - dc.SetBrush(*wxTRANSPARENT_BRUSH); - dc.DrawRectangle(32 - deadzone * 32, 0, deadzone * 64, 14); + gc->SetPen(*wxLIGHT_GREY_PEN); + gc->SetBrush(*wxTRANSPARENT_BRUSH); + gc->DrawRectangle(32 - deadzone * 32, 0, deadzone * 64, 14); } break; + default: break; } + gc->SetTransform(null_matrix); +} + +static void DrawBorder(wxGraphicsContext* gc, double scale) +{ + double pen_width = std::round(scale); // Pen width = 1px * scale + + // Use the window caption bar color as a safe accent color. + wxPen border_pen(wxSystemSettings::GetColour(wxSYS_COLOUR_ACTIVECAPTION), + static_cast<int>(pen_width)); + border_pen.SetCap(wxCAP_PROJECTING); + + double width, height; + gc->GetSize(&width, &height); + + wxGraphicsPath path = gc->CreatePath(); + path.AddRectangle(pen_width / 2, pen_width / 2, width - pen_width, height - pen_width); + gc->SetPen(border_pen); + gc->StrokePath(path); } void InputConfigDialog::UpdateBitmaps(wxTimerEvent& WXUNUSED(event)) { wxFont small_font(6, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD); + const wxColour font_color{0xB8B8B8}; g_controller_interface.UpdateInput(); GamepadPage* const current_page = - (GamepadPage*)m_pad_notebook->GetPage(m_pad_notebook->GetSelection()); + static_cast<GamepadPage*>(m_pad_notebook->GetPage(m_pad_notebook->GetSelection())); + wxMemoryDC dc; auto lock = ControllerEmu::GetStateLock(); for (ControlGroupBox* g : current_page->control_groups) { - // if this control group has a bitmap - if (g->static_bitmap) + // Only if this control group has a bitmap + if (!g->static_bitmap) + continue; + + wxBitmap bitmap(g->static_bitmap->GetBitmap()); + // NOTE: Selecting the bitmap inherits the bitmap's ScaleFactor onto the DC as well. + dc.SelectObject(bitmap); + dc.SetBackground(*wxWHITE_BRUSH); + dc.Clear(); + +#ifdef __WXGTK20__ + int dc_height = 0; + dc.SetFont(small_font); + dc.GetTextExtent(g->control_group->name, nullptr, &dc_height); +#endif + + std::unique_ptr<wxGraphicsContext> gc{wxGraphicsContext::Create(dc)}; + gc->DisableOffset(); + gc->SetFont(small_font, font_color); + +#ifdef __WXGTK20__ + double gc_height = 0; + gc->GetTextExtent(g->control_group->name, nullptr, &gc_height); + // On GTK2, wx creates a new empty Cairo/Pango context for the graphics context instead + // of reusing the wxMemoryDC one, this causes it to forget the screen DPI so fonts stop + // scaling, we need to scale it manually instead. + if (std::ceil(gc_height) < dc_height) { - wxMemoryDC dc; - wxBitmap bitmap(g->static_bitmap->GetBitmap()); - dc.SelectObject(bitmap); - dc.Clear(); - - dc.SetFont(small_font); - dc.SetTextForeground(0xC0C0C0); - - DrawControlGroupBox(dc, g); - - // box outline - // Windows XP color - dc.SetPen(wxPen("#7f9db9")); - dc.SetBrush(*wxTRANSPARENT_BRUSH); - dc.DrawRectangle(0, 0, bitmap.GetWidth(), bitmap.GetHeight()); - - // label for sticks and stuff - if (64 == bitmap.GetHeight()) - dc.DrawText(StrToWxStr(g->control_group->name).Upper(), 4, 2); - - dc.SelectObject(wxNullBitmap); - g->static_bitmap->SetBitmap(bitmap); + wxFont fixed_font(small_font); + fixed_font.SetPointSize(static_cast<int>(fixed_font.GetPointSize() * g->m_scale)); + gc->SetFont(fixed_font, font_color); } +#endif + + DrawControlGroupBox(gc.get(), g); + DrawBorder(gc.get(), g->m_scale); + + // label for sticks and stuff + if (g->HasBitmapHeading()) + gc->DrawText(StrToWxStr(g->control_group->name).Upper(), 4 * g->m_scale, 2 * g->m_scale); + + gc.reset(); + dc.SelectObject(wxNullBitmap); + g->static_bitmap->SetBitmap(bitmap); } } diff --git a/Source/Core/DolphinWX/LogConfigWindow.cpp b/Source/Core/DolphinWX/LogConfigWindow.cpp index da517d288f..52562507f6 100644 --- a/Source/Core/DolphinWX/LogConfigWindow.cpp +++ b/Source/Core/DolphinWX/LogConfigWindow.cpp @@ -25,18 +25,11 @@ LogConfigWindow::LogConfigWindow(wxWindow* parent, wxWindowID id) _("Log Configuration")), enableAll(true) { - Bind(wxEVT_CLOSE_WINDOW, &LogConfigWindow::OnClose, this); - SetMinSize(wxSize(100, 100)); m_LogManager = LogManager::GetInstance(); CreateGUIControls(); LoadSettings(); } -void LogConfigWindow::OnClose(wxCloseEvent& event) -{ - SaveSettings(); -} - void LogConfigWindow::CreateGUIControls() { // Verbosity @@ -68,23 +61,26 @@ void LogConfigWindow::CreateGUIControls() for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) m_checks->Append(StrToWxStr(m_LogManager->GetFullName((LogTypes::LOG_TYPE)i))); + const int space1 = FromDIP(1); + const int space5 = FromDIP(5); + // Sizers wxStaticBoxSizer* sbOutputs = new wxStaticBoxSizer(wxVERTICAL, this, _("Logger Outputs")); - sbOutputs->Add(m_writeFileCB, 0, wxDOWN, 1); - sbOutputs->Add(m_writeConsoleCB, 0, wxDOWN, 1); - sbOutputs->Add(m_writeWindowCB, 0); + sbOutputs->Add(m_writeFileCB, 0); + sbOutputs->Add(m_writeConsoleCB, 0, wxTOP, space1); + sbOutputs->Add(m_writeWindowCB, 0, wxTOP, space1); wxStaticBoxSizer* sbLogTypes = new wxStaticBoxSizer(wxVERTICAL, this, _("Log Types")); sbLogTypes->Add(m_checks, 1, wxEXPAND); wxBoxSizer* sMain = new wxBoxSizer(wxVERTICAL); - sMain->Add(m_verbosity, 0, wxEXPAND | wxLEFT | wxRIGHT, 5); - sMain->Add(sbOutputs, 0, wxEXPAND | wxLEFT | wxRIGHT, 5); - sMain->Add(btn_toggle_all, 0, wxEXPAND | wxLEFT | wxRIGHT, 5); - sMain->Add(sbLogTypes, 1, wxEXPAND | wxLEFT | wxRIGHT, 5); + sMain->Add(m_verbosity, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sMain->Add(sbOutputs, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sMain->Add(btn_toggle_all, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sMain->Add(sbLogTypes, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); - SetSizer(sMain); - Layout(); + sMain->SetMinSize(FromDIP(wxSize(100, 100))); + SetSizerAndFit(sMain); } void LogConfigWindow::LoadSettings() diff --git a/Source/Core/DolphinWX/LogConfigWindow.h b/Source/Core/DolphinWX/LogConfigWindow.h index 3259c060ec..027372880b 100644 --- a/Source/Core/DolphinWX/LogConfigWindow.h +++ b/Source/Core/DolphinWX/LogConfigWindow.h @@ -31,7 +31,6 @@ private: void CreateGUIControls(); void OnVerbosityChange(wxCommandEvent& event); - void OnClose(wxCloseEvent& event); void OnWriteFileChecked(wxCommandEvent& event); void OnWriteConsoleChecked(wxCommandEvent& event); void OnWriteWindowChecked(wxCommandEvent& event); diff --git a/Source/Core/DolphinWX/LogWindow.cpp b/Source/Core/DolphinWX/LogWindow.cpp index a971fe98e0..fff56dd260 100644 --- a/Source/Core/DolphinWX/LogWindow.cpp +++ b/Source/Core/DolphinWX/LogWindow.cpp @@ -39,7 +39,6 @@ CLogWindow::CLogWindow(CFrame* parent, wxWindowID id, const wxPoint& pos, const : wxPanel(parent, id, pos, size, style, name), x(0), y(0), winpos(0), Parent(parent), m_LogAccess(true), m_Log(nullptr), m_cmdline(nullptr), m_FontChoice(nullptr) { - Bind(wxEVT_CLOSE_WINDOW, &CLogWindow::OnClose, this); Bind(wxEVT_TIMER, &CLogWindow::OnLogTimer, this); m_LogManager = LogManager::GetInstance(); @@ -94,6 +93,7 @@ void CLogWindow::CreateGUIControls() m_LogManager->SetLogLevel((LogTypes::LOG_TYPE)i, (LogTypes::LOG_LEVELS)(verbosity)); } + m_has_listeners = true; // Font m_FontChoice = new wxChoice(this, wxID_ANY); @@ -132,11 +132,13 @@ void CLogWindow::CreateGUIControls() new wxButton(this, wxID_ANY, _("Clear"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); m_clear_log_btn->Bind(wxEVT_BUTTON, &CLogWindow::OnClear, this); + const int space3 = FromDIP(3); + // Sizers wxBoxSizer* sTop = new wxBoxSizer(wxHORIZONTAL); - sTop->Add(m_clear_log_btn); - sTop->Add(m_FontChoice, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 3); - sTop->Add(m_WrapLine, 0, wxALIGN_CENTER_VERTICAL); + sTop->Add(m_clear_log_btn, 0, wxALIGN_CENTER_VERTICAL); + sTop->Add(m_FontChoice, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space3); + sTop->Add(m_WrapLine, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space3); sBottom = new wxBoxSizer(wxVERTICAL); PopulateBottom(); @@ -149,15 +151,17 @@ void CLogWindow::CreateGUIControls() m_cmdline->SetFocus(); } -void CLogWindow::OnClose(wxCloseEvent& event) +CLogWindow::~CLogWindow() { - SaveSettings(); - event.Skip(); RemoveAllListeners(); } void CLogWindow::RemoveAllListeners() { + if (!m_has_listeners) + return; + m_has_listeners = false; + for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) { m_LogManager->RemoveListener(static_cast<LogTypes::LOG_TYPE>(i), diff --git a/Source/Core/DolphinWX/LogWindow.h b/Source/Core/DolphinWX/LogWindow.h index 61fa12f71c..26d931d52b 100644 --- a/Source/Core/DolphinWX/LogWindow.h +++ b/Source/Core/DolphinWX/LogWindow.h @@ -28,7 +28,11 @@ public: CLogWindow(CFrame* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL, const wxString& name = _("Log")); + ~CLogWindow() override; + // Listeners must be removed explicitly before the window is closed to prevent crashes on OS X + // when closing via the Dock. (The Core is probably being shutdown before the window) + void RemoveAllListeners(); void SaveSettings(); void Log(LogTypes::LOG_LEVELS, const char* text) override; @@ -42,6 +46,7 @@ private: LogManager* m_LogManager; std::queue<std::pair<u8, wxString>> msgQueue; bool m_writeFile, m_writeWindow, m_LogAccess; + bool m_has_listeners = false; // Controls wxBoxSizer* sBottom; @@ -57,11 +62,9 @@ private: void CreateGUIControls(); void PopulateBottom(); void UnPopulateBottom(); - void OnClose(wxCloseEvent& event); void OnFontChange(wxCommandEvent& event); void OnWrapLineCheck(wxCommandEvent& event); void OnClear(wxCommandEvent& event); void OnLogTimer(wxTimerEvent& WXUNUSED(event)); - void RemoveAllListeners(); void UpdateLog(); }; diff --git a/Source/Core/DolphinWX/Main.cpp b/Source/Core/DolphinWX/Main.cpp index 9eef4b3d0c..bdf6ada482 100644 --- a/Source/Core/DolphinWX/Main.cpp +++ b/Source/Core/DolphinWX/Main.cpp @@ -53,25 +53,6 @@ #include <X11/Xlib.h> #endif -#ifdef _WIN32 - -#ifndef SM_XVIRTUALSCREEN -#define SM_XVIRTUALSCREEN 76 -#endif -#ifndef SM_YVIRTUALSCREEN -#define SM_YVIRTUALSCREEN 77 -#endif -#ifndef SM_CXVIRTUALSCREEN -#define SM_CXVIRTUALSCREEN 78 -#endif -#ifndef SM_CYVIRTUALSCREEN -#define SM_CYVIRTUALSCREEN 79 -#endif - -#endif - -class wxFrame; - // ------------ // Main window @@ -130,31 +111,14 @@ bool DolphinApp::OnInit() // Enable the PNG image handler for screenshots wxImage::AddHandler(new wxPNGHandler); - int x = SConfig::GetInstance().iPosX; - int y = SConfig::GetInstance().iPosY; - int w = SConfig::GetInstance().iWidth; - int h = SConfig::GetInstance().iHeight; - -// The following is not needed with X11, where window managers -// do not allow windows to be created off the desktop. -#ifdef _WIN32 - // Out of desktop check - int leftPos = GetSystemMetrics(SM_XVIRTUALSCREEN); - int topPos = GetSystemMetrics(SM_YVIRTUALSCREEN); - int width = GetSystemMetrics(SM_CXVIRTUALSCREEN); - int height = GetSystemMetrics(SM_CYVIRTUALSCREEN); - if ((leftPos + width) < (x + w) || leftPos > x || (topPos + height) < (y + h) || topPos > y) - x = y = wxDefaultCoord; -#elif defined __APPLE__ - if (y < 1) - y = wxDefaultCoord; -#endif - - main_frame = new CFrame(nullptr, wxID_ANY, StrToWxStr(scm_rev_str), wxPoint(x, y), wxSize(w, h), + // We have to copy the size and position out of SConfig now because CFrame's OnMove + // handler will corrupt them during window creation (various APIs like SetMenuBar cause + // event dispatch including WM_MOVE/WM_SIZE) + wxRect window_geometry(SConfig::GetInstance().iPosX, SConfig::GetInstance().iPosY, + SConfig::GetInstance().iWidth, SConfig::GetInstance().iHeight); + main_frame = new CFrame(nullptr, wxID_ANY, StrToWxStr(scm_rev_str), window_geometry, m_use_debugger, m_batch_mode, m_use_logger); - SetTopWindow(main_frame); - main_frame->SetMinSize(wxSize(400, 300)); AfterInit(); diff --git a/Source/Core/DolphinWX/MemcardManager.cpp b/Source/Core/DolphinWX/MemcardManager.cpp index d181e1da07..f5da01a43a 100644 --- a/Source/Core/DolphinWX/MemcardManager.cpp +++ b/Source/Core/DolphinWX/MemcardManager.cpp @@ -17,7 +17,6 @@ #include <wx/listbase.h> #include <wx/menu.h> #include <wx/msgdlg.h> -#include <wx/mstream.h> #include <wx/sizer.h> #include <wx/stattext.h> @@ -34,37 +33,24 @@ #define FIRSTPAGE 0 #define ARROWS slot ? "" : ARROW[slot], slot ? ARROW[slot] : "" -static wxBitmap wxBitmapFromMemoryRGBA(const unsigned char* data, u32 width, u32 height) +static wxImage wxImageFromMemoryRGBA(const u32* data, u32 width, u32 height) { - static const std::array<u8, 54> header = { - {0x42, 0x4D, 0x38, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, - 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // Width - 0x20, 0x00, 0x00, 0x00, // Height - 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, // Data size - 0x12, 0x0B, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00}}; - - u32 stride = (4 * width); - - u32 bytes = (stride * height) + header.size(); - bytes = (bytes + 3) & ~3; - - u32 data_length = bytes - header.size(); - - std::vector<u8> pdata(bytes); - std::copy(header.begin(), header.end(), pdata.begin()); - - u8* const pixelData = &pdata[header.size()]; - - for (u32 y = 0; y < height; y++) - std::memcpy(&pixelData[y * stride], &data[(height - y - 1) * stride], stride); - - std::memcpy(&pdata[18], &width, sizeof(u32)); - std::memcpy(&pdata[22], &height, sizeof(u32)); - std::memcpy(&pdata[34], &data_length, sizeof(u32)); - - wxMemoryInputStream is(pdata.data(), bytes); - return wxBitmap(wxImage(is, wxBITMAP_TYPE_BMP)); + wxImage image(width, height, false); + image.InitAlpha(); + u8* rgb = image.GetData(); + u8* alpha = image.GetAlpha(); + for (u32 y = 0; y < height; ++y) + { + for (u32 x = 0; x < width; ++x) + { + u32 pixel = data[y * width + x]; + *rgb++ = (pixel & 0x00FF0000) >> 16; // Red + *rgb++ = (pixel & 0x0000FF00) >> 8; // Green + *rgb++ = (pixel & 0x000000FF) >> 0; // Blue + *alpha++ = (pixel & 0xFF000000) >> 24; + } + } + return image; } BEGIN_EVENT_TABLE(CMemcardManager, wxDialog) @@ -94,7 +80,8 @@ END_EVENT_TABLE() CMemcardManager::CMemcardManager(wxWindow* parent) : wxDialog(parent, wxID_ANY, _("Memory Card Manager"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxSYSTEM_MENU | wxDIALOG_NO_PARENT | wxCLOSE_BOX | wxRESIZE_BORDER | - wxMAXIMIZE_BOX) + wxMAXIMIZE_BOX), + m_image_list_size(FromDIP(wxSize(96, 32))) { memoryCard[SLOT_A] = nullptr; memoryCard[SLOT_B] = nullptr; @@ -179,6 +166,7 @@ bool CMemcardManager::SaveSettings() void CMemcardManager::CreateGUIControls() { // Create the controls for both memcards + const int space5 = FromDIP(5); const char* ARROW[2] = {"<-", "->"}; @@ -203,11 +191,11 @@ void CMemcardManager::CreateGUIControls() t_Status[slot] = new wxStaticText(this, 0, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, wxEmptyString); - wxBoxSizer* const sPages = new wxBoxSizer(wxHORIZONTAL); - sPages->Add(m_PrevPage[slot], 0, wxEXPAND | wxALL, 1); - sPages->Add(t_Status[slot], 0, wxEXPAND | wxALL, 5); - sPages->Add(0, 0, 1, wxEXPAND | wxALL, 0); - sPages->Add(m_NextPage[slot], 0, wxEXPAND | wxALL, 1); + wxFlexGridSizer* const paging_sizer = new wxFlexGridSizer(3, wxSize(space5, space5)); + paging_sizer->AddGrowableCol(1); + paging_sizer->Add(m_PrevPage[slot], 0, wxEXPAND); + paging_sizer->Add(t_Status[slot], 0, wxALIGN_CENTER); + paging_sizer->Add(m_NextPage[slot], 0, wxEXPAND); m_MemcardPath[slot] = new wxFilePickerCtrl( this, ID_MEMCARDPATH_A + slot, StrToWxStr(File::GetUserPath(D_GCUSER_IDX)), @@ -216,41 +204,51 @@ void CMemcardManager::CreateGUIControls() wxDefaultSize, wxFLP_USE_TEXTCTRL | wxFLP_OPEN); m_MemcardList[slot] = new CMemcardListCtrl( - this, ID_MEMCARDLIST_A + slot, wxDefaultPosition, wxSize(350, 400), + this, ID_MEMCARDLIST_A + slot, wxDefaultPosition, FromDIP(wxSize(350, 400)), wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT | wxLC_SINGLE_SEL, mcmSettings); - m_MemcardList[slot]->AssignImageList(new wxImageList(96, 32), wxIMAGE_LIST_SMALL); + m_MemcardList[slot]->AssignImageList( + new wxImageList(m_image_list_size.GetWidth(), m_image_list_size.GetHeight()), + wxIMAGE_LIST_SMALL); sMemcard[slot] = new wxStaticBoxSizer(wxVERTICAL, this, _("Memory Card") + wxString::Format(" %c", 'A' + slot)); - sMemcard[slot]->Add(m_MemcardPath[slot], 0, wxEXPAND | wxALL, 5); - sMemcard[slot]->Add(m_MemcardList[slot], 1, wxEXPAND | wxALL, 5); - sMemcard[slot]->Add(sPages, 0, wxEXPAND | wxALL, 1); + sMemcard[slot]->AddSpacer(space5); + sMemcard[slot]->Add(m_MemcardPath[slot], 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sMemcard[slot]->AddSpacer(space5); + sMemcard[slot]->Add(m_MemcardList[slot], 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + sMemcard[slot]->AddSpacer(space5); + sMemcard[slot]->Add(paging_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sMemcard[slot]->AddSpacer(space5); } wxBoxSizer* const sButtons = new wxBoxSizer(wxVERTICAL); sButtons->AddStretchSpacer(2); - sButtons->Add(m_CopyFrom[SLOT_B], 0, wxEXPAND, 5); - sButtons->Add(m_CopyFrom[SLOT_A], 0, wxEXPAND, 5); - sButtons->AddStretchSpacer(1); - sButtons->Add(m_SaveImport[SLOT_A], 0, wxEXPAND, 5); - sButtons->Add(m_SaveExport[SLOT_A], 0, wxEXPAND, 5); - sButtons->AddStretchSpacer(1); - sButtons->Add(m_ConvertToGci, 0, wxEXPAND, 5); - sButtons->AddStretchSpacer(1); - sButtons->Add(m_SaveImport[SLOT_B], 0, wxEXPAND, 5); - sButtons->Add(m_SaveExport[SLOT_B], 0, wxEXPAND, 5); - sButtons->AddStretchSpacer(1); - sButtons->Add(m_Delete[SLOT_A], 0, wxEXPAND, 5); - sButtons->Add(m_Delete[SLOT_B], 0, wxEXPAND, 5); + sButtons->Add(m_CopyFrom[SLOT_B], 0, wxEXPAND); + sButtons->Add(m_CopyFrom[SLOT_A], 0, wxEXPAND); sButtons->AddStretchSpacer(); - sButtons->Add(new wxButton(this, wxID_OK, _("Close")), 0, wxEXPAND, 5); + sButtons->Add(m_SaveImport[SLOT_A], 0, wxEXPAND); + sButtons->Add(m_SaveExport[SLOT_A], 0, wxEXPAND); + sButtons->AddStretchSpacer(); + sButtons->Add(m_ConvertToGci, 0, wxEXPAND); + sButtons->AddStretchSpacer(); + sButtons->Add(m_SaveImport[SLOT_B], 0, wxEXPAND); + sButtons->Add(m_SaveExport[SLOT_B], 0, wxEXPAND); + sButtons->AddStretchSpacer(); + sButtons->Add(m_Delete[SLOT_A], 0, wxEXPAND); + sButtons->Add(m_Delete[SLOT_B], 0, wxEXPAND); + sButtons->AddStretchSpacer(); + sButtons->Add(new wxButton(this, wxID_OK, _("Close")), 0, wxEXPAND); sButtons->AddStretchSpacer(); wxBoxSizer* const sMain = new wxBoxSizer(wxHORIZONTAL); - sMain->Add(sMemcard[SLOT_A], 1, wxEXPAND | wxALL, 5); - sMain->Add(sButtons, 0, wxEXPAND, 0); - sMain->Add(sMemcard[SLOT_B], 1, wxEXPAND | wxALL, 5); + sMain->AddSpacer(space5); + sMain->Add(sMemcard[SLOT_A], 1, wxEXPAND | wxTOP | wxBOTTOM, space5); + sMain->AddSpacer(space5); + sMain->Add(sButtons, 0, wxEXPAND | wxTOP | wxBOTTOM, space5); + sMain->AddSpacer(space5); + sMain->Add(sMemcard[SLOT_B], 1, wxEXPAND | wxTOP | wxBOTTOM, space5); + sMain->AddSpacer(space5); SetSizerAndFit(sMain); Center(); @@ -653,57 +651,73 @@ bool CMemcardManager::ReloadMemcard(const std::string& fileName, int card) u8 nFiles = memoryCard[card]->GetNumFiles(); std::vector<int> images(nFiles * 2); + static constexpr unsigned int BANNER_WIDTH = 96; + static constexpr unsigned int ANIM_FRAME_WIDTH = 32; + static constexpr unsigned int IMAGE_HEIGHT = 32; + static constexpr unsigned int ANIM_MAX_FRAMES = 8; + + std::vector<u32> pxdata(BANNER_WIDTH * IMAGE_HEIGHT); + std::vector<u8> anim_delay(ANIM_MAX_FRAMES); + std::vector<u32> anim_data(ANIM_FRAME_WIDTH * IMAGE_HEIGHT * ANIM_MAX_FRAMES); for (u8 i = 0; i < nFiles; i++) { - static u32 pxdata[96 * 32]; - static u8 animDelay[8]; - static u32 animData[32 * 32 * 8]; + u8 file_index = memoryCard[card]->GetFileIndex(i); + u32 num_frames = + memoryCard[card]->ReadAnimRGBA8(file_index, anim_data.data(), anim_delay.data()); - u8 fileIndex = memoryCard[card]->GetFileIndex(i); - int numFrames = memoryCard[card]->ReadAnimRGBA8(fileIndex, animData, animDelay); - - if (!memoryCard[card]->ReadBannerRGBA8(fileIndex, pxdata)) + // Decode Save File Animation + wxImage anim_img_strip; + if (num_frames > 0) { - memset(pxdata, 0, 96 * 32 * 4); + unsigned int frames = BANNER_WIDTH / ANIM_FRAME_WIDTH; - if (numFrames > 0) // Just use the first one + if (num_frames < frames) { - u32* icdata = animData; + frames = num_frames; - for (int y = 0; y < 32; y++) + // Clear unused frame's pixels from the buffer. + std::fill(pxdata.begin(), pxdata.end(), 0); + } + + for (unsigned int f = 0; f < frames; ++f) + { + for (unsigned int y = 0; y < IMAGE_HEIGHT; ++y) { - for (int x = 0; x < 32; x++) + for (unsigned int x = 0; x < ANIM_FRAME_WIDTH; ++x) { - pxdata[y * 96 + x + 32] = icdata[y * 32 + x]; // | 0xFF000000 + // NOTE: pxdata is stacked horizontal, anim_data is stacked vertical + pxdata[y * BANNER_WIDTH + f * ANIM_FRAME_WIDTH + x] = + anim_data[f * ANIM_FRAME_WIDTH * IMAGE_HEIGHT + y * IMAGE_HEIGHT + x]; } } } + anim_img_strip = wxImageFromMemoryRGBA(pxdata.data(), BANNER_WIDTH, IMAGE_HEIGHT); } - - wxBitmap map = wxBitmapFromMemoryRGBA((u8*)pxdata, 96, 32); - images[i * 2] = list->Add(map); - - if (numFrames > 0) + else { - memset(pxdata, 0, 96 * 32 * 4); - int frames = 3; - - if (numFrames < frames) - frames = numFrames; - - for (int f = 0; f < frames; f++) - { - for (int y = 0; y < 32; y++) - { - for (int x = 0; x < 32; x++) - { - pxdata[y * 96 + x + 32 * f] = animData[f * 32 * 32 + y * 32 + x]; - } - } - } - wxBitmap icon = wxBitmapFromMemoryRGBA((u8*)pxdata, 96, 32); - images[i * 2 + 1] = list->Add(icon); + // No Animation found, use an empty placeholder instead. + anim_img_strip.Create(BANNER_WIDTH, IMAGE_HEIGHT, false); + anim_img_strip.Clear(0xFF); + anim_img_strip.SetMaskColour(0xFF, 0xFF, 0xFF); } + + // Decode Banner if it exists + { + wxImage image; + if (memoryCard[card]->ReadBannerRGBA8(file_index, pxdata.data())) + { + image = wxImageFromMemoryRGBA(pxdata.data(), BANNER_WIDTH, IMAGE_HEIGHT); + } + else + { + // Use first frame of animation instead. + image = anim_img_strip.Size(wxSize(ANIM_FRAME_WIDTH, IMAGE_HEIGHT), wxPoint(0, 0)); + } + images[i * 2] = list->Add(WxUtils::ScaleImageToBitmap(image, this, m_image_list_size)); + } + + images[i * 2 + 1] = + list->Add(WxUtils::ScaleImageToBitmap(anim_img_strip, this, m_image_list_size)); } int pagesMax = (mcmSettings.usePages) ? (page[card] + 1) * itemsPerPage : 128; diff --git a/Source/Core/DolphinWX/MemcardManager.h b/Source/Core/DolphinWX/MemcardManager.h index 3f307117b9..edfd5fac74 100644 --- a/Source/Core/DolphinWX/MemcardManager.h +++ b/Source/Core/DolphinWX/MemcardManager.h @@ -131,5 +131,6 @@ private: void OnRightClick(wxMouseEvent& event); }; + wxSize m_image_list_size; CMemcardListCtrl* m_MemcardList[2]; }; diff --git a/Source/Core/DolphinWX/NetPlay/ChangeGameDialog.cpp b/Source/Core/DolphinWX/NetPlay/ChangeGameDialog.cpp index be1173e12e..b685e89b08 100644 --- a/Source/Core/DolphinWX/NetPlay/ChangeGameDialog.cpp +++ b/Source/Core/DolphinWX/NetPlay/ChangeGameDialog.cpp @@ -12,6 +12,8 @@ ChangeGameDialog::ChangeGameDialog(wxWindow* parent, const CGameListCtrl* const game_list) : wxDialog(parent, wxID_ANY, _("Select Game")) { + const int space5 = FromDIP(5); + m_game_lbox = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxLB_SORT); m_game_lbox->Bind(wxEVT_LISTBOX_DCLICK, &ChangeGameDialog::OnPick, this); @@ -22,8 +24,11 @@ ChangeGameDialog::ChangeGameDialog(wxWindow* parent, const CGameListCtrl* const ok_btn->Bind(wxEVT_BUTTON, &ChangeGameDialog::OnPick, this); wxBoxSizer* const szr = new wxBoxSizer(wxVERTICAL); - szr->Add(m_game_lbox, 1, wxLEFT | wxRIGHT | wxTOP | wxEXPAND, 5); - szr->Add(ok_btn, 0, wxALL | wxALIGN_RIGHT, 5); + szr->AddSpacer(space5); + szr->Add(m_game_lbox, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + szr->AddSpacer(space5); + szr->Add(ok_btn, 0, wxALIGN_RIGHT | wxLEFT | wxRIGHT, space5); + szr->AddSpacer(space5); SetSizerAndFit(szr); SetFocus(); diff --git a/Source/Core/DolphinWX/NetPlay/MD5Dialog.cpp b/Source/Core/DolphinWX/NetPlay/MD5Dialog.cpp index 2eb8547693..c2e7ccfd4f 100644 --- a/Source/Core/DolphinWX/NetPlay/MD5Dialog.cpp +++ b/Source/Core/DolphinWX/NetPlay/MD5Dialog.cpp @@ -7,6 +7,7 @@ #include <wx/gauge.h> #include <wx/panel.h> #include <wx/sizer.h> +#include <wx/statbox.h> #include <wx/stattext.h> #include "Common/StringUtil.h" @@ -17,43 +18,54 @@ MD5Dialog::MD5Dialog(wxWindow* parent, NetPlayServer* server, std::vector<const const std::string& game) : wxDialog(parent, wxID_ANY, _("MD5 Checksum")), m_netplay_server(server) { - Bind(wxEVT_CLOSE_WINDOW, &MD5Dialog::OnClose, this); + const int space5 = FromDIP(5); + wxBoxSizer* const main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->Add(new wxStaticText(this, wxID_ANY, _("Computing MD5 Checksum for:") + "\n" + game, - wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL), - 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5); + main_sizer->AddSpacer(space5); + main_sizer->Add(new wxStaticText(this, wxID_ANY, + wxString::Format(_("Computing MD5 Checksum for:\n%s"), game), + wxDefaultPosition, wxDefaultSize, + wxALIGN_CENTRE_HORIZONTAL | wxST_NO_AUTORESIZE), + 0, wxEXPAND | wxLEFT | wxRIGHT, space5); for (const Player* player : players) { wxStaticBoxSizer* const player_szr = new wxStaticBoxSizer( wxVERTICAL, this, player->name + " (p" + std::to_string(player->pid) + ")"); - wxGauge* gauge = new wxGauge(this, wxID_ANY, 100); + wxGauge* gauge = new wxGauge(player_szr->GetStaticBox(), wxID_ANY, 100); m_progress_bars[player->pid] = gauge; - player_szr->Add(gauge, 0, wxEXPAND | wxALL, 5); m_result_labels[player->pid] = - new wxStaticText(this, wxID_ANY, _("Computing..."), wxDefaultPosition, wxSize(250, 20), - wxALIGN_CENTRE_HORIZONTAL); + new wxStaticText(player_szr->GetStaticBox(), wxID_ANY, _("Computing...")); - m_result_labels[player->pid]->SetSize(250, 15); - player_szr->Add(m_result_labels[player->pid], 0, wxALL, 5); + player_szr->AddSpacer(space5); + player_szr->Add(gauge, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + player_szr->AddSpacer(space5); + player_szr->Add(m_result_labels[player->pid], 0, wxALIGN_CENTER_HORIZONTAL | wxLEFT | wxRIGHT, + space5); + player_szr->AddSpacer(space5); + player_szr->SetMinSize(FromDIP(wxSize(250, -1))); - main_sizer->Add(player_szr, 0, wxEXPAND | wxALL, 5); + main_sizer->AddSpacer(space5); + main_sizer->Add(player_szr, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); } m_final_result_label = new wxStaticText(this, wxID_ANY, " ", // so it takes space wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL); - main_sizer->Add(m_final_result_label, 1, wxALL, 5); - - wxButton* close_btn = new wxButton(this, wxID_ANY, _("Close")); - close_btn->Bind(wxEVT_BUTTON, &MD5Dialog::OnCloseBtnPressed, this); - main_sizer->Add(close_btn, 0, wxEXPAND | wxALL, 5); + main_sizer->AddSpacer(space5); + main_sizer->Add(m_final_result_label, 1, wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); + main_sizer->Add(CreateStdDialogButtonSizer(wxCLOSE), 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_sizer->AddSpacer(space5); SetSizerAndFit(main_sizer); + + Bind(wxEVT_BUTTON, &MD5Dialog::OnCloseBtnPressed, this, wxID_CLOSE); + Bind(wxEVT_CLOSE_WINDOW, &MD5Dialog::OnClose, this); SetFocus(); Center(); } @@ -65,6 +77,7 @@ void MD5Dialog::SetProgress(int pid, int progress) m_progress_bars[pid]->SetValue(progress); m_result_labels[pid]->SetLabel(_("Computing: ") + std::to_string(progress) + "%"); + Layout(); Update(); } @@ -76,11 +89,12 @@ void MD5Dialog::SetResult(int pid, const std::string& result) m_result_labels[pid]->SetLabel(result); m_hashes.push_back(result); - if (m_hashes.size() <= 1) - return; - - wxString label = AllHashesMatch() ? _("Hashes match!") : _("Hashes do not match."); - m_final_result_label->SetLabel(label); + if (m_hashes.size() > 1) + { + wxString label = AllHashesMatch() ? _("Hashes match!") : _("Hashes do not match."); + m_final_result_label->SetLabel(label); + } + Layout(); } bool MD5Dialog::AllHashesMatch() const @@ -89,7 +103,7 @@ bool MD5Dialog::AllHashesMatch() const m_hashes.end(); } -void MD5Dialog::OnClose(wxCloseEvent& event) +void MD5Dialog::OnClose(wxCloseEvent&) { m_netplay_server->AbortMD5(); } diff --git a/Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.cpp b/Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.cpp index c9f0e0f86b..21dbce3217 100644 --- a/Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.cpp +++ b/Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.cpp @@ -5,6 +5,7 @@ #include <wx/button.h> #include <wx/checkbox.h> #include <wx/choice.h> +#include <wx/gbsizer.h> #include <wx/listbox.h> #include <wx/notebook.h> #include <wx/panel.h> @@ -41,50 +42,50 @@ NetPlaySetupFrame::NetPlaySetupFrame(wxWindow* const parent, const CGameListCtrl inifile.Load(File::GetUserPath(F_DOLPHINCONFIG_IDX)); IniFile::Section& netplay_section = *inifile.GetOrCreateSection("NetPlay"); - wxPanel* const panel = new wxPanel(this); - panel->Bind(wxEVT_CHAR_HOOK, &NetPlaySetupFrame::OnKeyDown, this); + CreateGUI(); + SetIcons(WxUtils::GetDolphinIconBundle()); - // top row - wxBoxSizer* const trav_szr = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* const nick_szr = new wxBoxSizer(wxHORIZONTAL); { - // Connection Config - wxStaticText* const connectiontype_lbl = new wxStaticText( - panel, wxID_ANY, _("Connection Type:"), wxDefaultPosition, wxSize(100, -1)); + std::string temp; + netplay_section.Get("Nickname", &temp, "Player"); + m_nickname_text->SetValue(StrToWxStr(temp)); - m_direct_traversal = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxSize(150, -1)); - m_direct_traversal->Bind(wxEVT_CHOICE, &NetPlaySetupFrame::OnDirectTraversalChoice, this); - m_direct_traversal->Append(_("Direct Connection")); - m_direct_traversal->Append(_("Traversal Server")); + temp.clear(); + netplay_section.Get("HostCode", &temp, "00000000"); + m_connect_hashcode_text->SetValue(StrToWxStr(temp)); - trav_szr->Add(connectiontype_lbl, 0, wxCENTER, 5); - trav_szr->AddSpacer(5); - trav_szr->Add(m_direct_traversal, 0, wxCENTER, 5); + temp.clear(); + netplay_section.Get("Address", &temp, "127.0.0.1"); + m_connect_ip_text->SetValue(StrToWxStr(temp)); - m_trav_reset_btn = new wxButton(panel, wxID_ANY, _("Reset Traversal Settings"), - wxDefaultPosition, wxSize(-1, 25)); - m_trav_reset_btn->Bind(wxEVT_BUTTON, &NetPlaySetupFrame::OnResetTraversal, this); + temp.clear(); + netplay_section.Get("ConnectPort", &temp, + std::to_string(NetPlayHostConfig::DEFAULT_LISTEN_PORT)); + m_connect_port_text->SetValue(StrToWxStr(temp)); - trav_szr->AddSpacer(5); + temp.clear(); + netplay_section.Get("HostPort", &temp, std::to_string(NetPlayHostConfig::DEFAULT_LISTEN_PORT)); + m_host_port_text->SetValue(StrToWxStr(temp)); - trav_szr->Add(m_trav_reset_btn, 0, wxRIGHT); + temp.clear(); + if (netplay_section.Get("SelectedHostGame", &temp, "")) + m_game_lbox->SetStringSelection(StrToWxStr(temp)); - // Nickname - wxStaticText* const nick_lbl = - new wxStaticText(panel, wxID_ANY, _("Nickname:"), wxDefaultPosition, wxSize(100, -1)); +#ifdef USE_UPNP + bool use_upnp = false; + netplay_section.Get("UseUPNP", &use_upnp, false); + m_upnp_chk->SetValue(use_upnp); +#endif - std::string nickname; - netplay_section.Get("Nickname", &nickname, "Player"); + unsigned int listen_port = 0; + netplay_section.Get("ListenPort", &listen_port, 0); + m_traversal_listen_port_enabled->SetValue(listen_port != 0); + m_traversal_listen_port->Enable(m_traversal_listen_port_enabled->IsChecked()); + m_traversal_listen_port->SetValue(listen_port); - m_nickname_text = - new wxTextCtrl(panel, wxID_ANY, StrToWxStr(nickname), wxDefaultPosition, wxSize(150, -1)); - - nick_szr->Add(nick_lbl, 0, wxCENTER); - nick_szr->Add(m_nickname_text, 0, wxALL, 5); - - std::string travChoice; - netplay_section.Get("TraversalChoice", &travChoice, "direct"); - if (travChoice == "traversal") + temp.clear(); + netplay_section.Get("TraversalChoice", &temp, "direct"); + if (temp == "traversal") { m_direct_traversal->Select(TRAVERSAL_CHOICE); } @@ -93,37 +94,96 @@ NetPlaySetupFrame::NetPlaySetupFrame(wxWindow* const parent, const CGameListCtrl m_direct_traversal->Select(DIRECT_CHOICE); } - m_traversal_lbl = new wxStaticText(panel, wxID_ANY, GetTraversalLabelText(netplay_section)); + m_traversal_lbl->SetLabelText(GetTraversalLabelText(netplay_section)); } - // tabs - m_notebook = new wxNotebook(panel, wxID_ANY); - wxPanel* const connect_tab = new wxPanel(m_notebook, wxID_ANY); - m_notebook->AddPage(connect_tab, _("Connect")); - wxPanel* const host_tab = new wxPanel(m_notebook, wxID_ANY); - m_notebook->AddPage(host_tab, _("Host")); + + Center(); + Show(); + + // Needs to be done last or it set up the spacing on the page correctly + wxCommandEvent ev; + OnDirectTraversalChoice(ev); +} + +void NetPlaySetupFrame::CreateGUI() +{ + const int space5 = FromDIP(5); + + wxPanel* const panel = new wxPanel(this); + panel->Bind(wxEVT_CHAR_HOOK, &NetPlaySetupFrame::OnKeyDown, this); + + // Connection Config + wxStaticText* const connectiontype_lbl = new wxStaticText(panel, wxID_ANY, _("Connection Type:")); + + m_direct_traversal = new wxChoice(panel, wxID_ANY); + m_direct_traversal->Bind(wxEVT_CHOICE, &NetPlaySetupFrame::OnDirectTraversalChoice, this); + m_direct_traversal->Append(_("Direct Connection")); + m_direct_traversal->Append(_("Traversal Server")); + + m_trav_reset_btn = new wxButton(panel, wxID_ANY, _("Reset Traversal Settings")); + m_trav_reset_btn->Bind(wxEVT_BUTTON, &NetPlaySetupFrame::OnResetTraversal, this); + + // Nickname + wxStaticText* const nick_lbl = new wxStaticText(panel, wxID_ANY, _("Nickname:")); + + m_nickname_text = new wxTextCtrl(panel, wxID_ANY, "Player"); + + m_traversal_lbl = new wxStaticText(panel, wxID_ANY, "Traversal Server"); + + wxButton* const quit_btn = new wxButton(panel, wxID_ANY, _("Quit")); + quit_btn->Bind(wxEVT_BUTTON, &NetPlaySetupFrame::OnQuit, this); + + wxGridBagSizer* top_sizer = new wxGridBagSizer(space5, space5); + top_sizer->Add(connectiontype_lbl, wxGBPosition(0, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + top_sizer->Add(WxUtils::GiveMinSizeDIP(m_direct_traversal, wxSize(100, -1)), wxGBPosition(0, 1), + wxDefaultSpan, wxEXPAND); + top_sizer->Add(m_trav_reset_btn, wxGBPosition(0, 2), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + top_sizer->Add(nick_lbl, wxGBPosition(1, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + top_sizer->Add(WxUtils::GiveMinSizeDIP(m_nickname_text, wxSize(150, -1)), wxGBPosition(1, 1), + wxDefaultSpan, wxEXPAND); + + m_notebook = CreateNotebookGUI(panel); + m_notebook->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &NetPlaySetupFrame::OnTabChanged, this); + + // main sizer + wxBoxSizer* const main_szr = new wxBoxSizer(wxVERTICAL); + main_szr->AddSpacer(space5); + main_szr->Add(top_sizer, 0, wxLEFT | wxRIGHT, space5); + main_szr->AddSpacer(space5); + main_szr->Add(m_traversal_lbl, 0, wxLEFT | wxRIGHT, space5); + main_szr->AddSpacer(space5); + main_szr->Add(m_notebook, 1, wxLEFT | wxRIGHT | wxEXPAND, space5); + main_szr->AddSpacer(space5); + main_szr->Add(quit_btn, 0, wxALIGN_RIGHT | wxLEFT | wxRIGHT, space5); + main_szr->AddSpacer(space5); + + panel->SetSizerAndFit(main_szr); + main_szr->SetSizeHints(this); +} + +wxNotebook* NetPlaySetupFrame::CreateNotebookGUI(wxWindow* parent) +{ + const int space5 = FromDIP(5); + + wxNotebook* const notebook = new wxNotebook(parent, wxID_ANY); + wxPanel* const connect_tab = new wxPanel(notebook, wxID_ANY); + notebook->AddPage(connect_tab, _("Connect")); + wxPanel* const host_tab = new wxPanel(notebook, wxID_ANY); + notebook->AddPage(host_tab, _("Host")); // connect tab { m_ip_lbl = new wxStaticText(connect_tab, wxID_ANY, _("Host Code :")); - - std::string last_hash_code; - netplay_section.Get("HostCode", &last_hash_code, "00000000"); - std::string last_ip_address; - netplay_section.Get("Address", &last_ip_address, "127.0.0.1"); - m_connect_ip_text = new wxTextCtrl(connect_tab, wxID_ANY, StrToWxStr(last_ip_address)); - m_connect_hashcode_text = new wxTextCtrl(connect_tab, wxID_ANY, StrToWxStr(last_hash_code)); + m_connect_ip_text = new wxTextCtrl(connect_tab, wxID_ANY, "127.0.0.1"); + m_connect_hashcode_text = new wxTextCtrl(connect_tab, wxID_ANY, "00000000"); // Will be overridden by OnDirectTraversalChoice, but is necessary // so that both inputs do not take up space m_connect_hashcode_text->Hide(); m_client_port_lbl = new wxStaticText(connect_tab, wxID_ANY, _("Port :")); - - // string? w/e - std::string port; - netplay_section.Get("ConnectPort", &port, - std::to_string(NetPlayHostConfig::DEFAULT_LISTEN_PORT)); - m_connect_port_text = new wxTextCtrl(connect_tab, wxID_ANY, StrToWxStr(port)); + m_connect_port_text = new wxTextCtrl(connect_tab, wxID_ANY, + std::to_string(NetPlayHostConfig::DEFAULT_LISTEN_PORT)); wxButton* const connect_btn = new wxButton(connect_tab, wxID_ANY, _("Connect")); connect_btn->Bind(wxEVT_BUTTON, &NetPlaySetupFrame::OnJoin, this); @@ -139,19 +199,22 @@ NetPlaySetupFrame::NetPlaySetupFrame(wxWindow* const parent, const CGameListCtrl "Wiimote netplay is experimental and should not be expected to work.\n")); wxBoxSizer* const top_szr = new wxBoxSizer(wxHORIZONTAL); - - top_szr->Add(m_ip_lbl, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5); - top_szr->Add(m_connect_ip_text, 3); - top_szr->Add(m_connect_hashcode_text, 3); - top_szr->Add(m_client_port_lbl, 0, wxCENTER | wxRIGHT | wxLEFT, 5); - top_szr->Add(m_connect_port_text, 1); + top_szr->Add(m_ip_lbl, 0, wxALIGN_CENTER_VERTICAL); + top_szr->Add(m_connect_ip_text, 3, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); + top_szr->Add(m_connect_hashcode_text, 3, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); + top_szr->Add(m_client_port_lbl, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); + top_szr->Add(m_connect_port_text, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); wxBoxSizer* const con_szr = new wxBoxSizer(wxVERTICAL); - con_szr->Add(top_szr, 0, wxALL | wxEXPAND, 5); + con_szr->AddSpacer(space5); + con_szr->Add(top_szr, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); con_szr->AddStretchSpacer(1); - con_szr->Add(alert_lbl, 0, wxLEFT | wxRIGHT | wxEXPAND, 5); + con_szr->AddSpacer(space5); + con_szr->Add(alert_lbl, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); con_szr->AddStretchSpacer(1); - con_szr->Add(connect_btn, 0, wxALL | wxALIGN_RIGHT, 5); + con_szr->AddSpacer(space5); + con_szr->Add(connect_btn, 0, wxALIGN_RIGHT | wxLEFT | wxRIGHT, space5); + con_szr->AddSpacer(space5); connect_tab->SetSizerAndFit(con_szr); } @@ -159,21 +222,13 @@ NetPlaySetupFrame::NetPlaySetupFrame(wxWindow* const parent, const CGameListCtrl // host tab { m_host_port_lbl = new wxStaticText(host_tab, wxID_ANY, _("Port :")); - - // string? w/e - std::string port; - netplay_section.Get("HostPort", &port, std::to_string(NetPlayHostConfig::DEFAULT_LISTEN_PORT)); - m_host_port_text = new wxTextCtrl(host_tab, wxID_ANY, StrToWxStr(port)); + m_host_port_text = + new wxTextCtrl(host_tab, wxID_ANY, std::to_string(NetPlayHostConfig::DEFAULT_LISTEN_PORT)); m_traversal_listen_port_enabled = new wxCheckBox(host_tab, wxID_ANY, _("Force Listen Port: ")); m_traversal_listen_port = new wxSpinCtrl(host_tab, wxID_ANY, "", wxDefaultPosition, - wxSize(80, -1), wxSP_ARROW_KEYS, 1, 65535); - - unsigned int listen_port; - netplay_section.Get("ListenPort", &listen_port, 0); - m_traversal_listen_port_enabled->SetValue(listen_port != 0); - m_traversal_listen_port->Enable(m_traversal_listen_port_enabled->IsChecked()); - m_traversal_listen_port->SetValue(listen_port); + wxDefaultSize, wxSP_ARROW_KEYS, 1, 65535); + m_traversal_listen_port->SetMinSize(WxUtils::GetTextWidgetMinSize(m_traversal_listen_port)); m_traversal_listen_port_enabled->Bind(wxEVT_CHECKBOX, &NetPlaySetupFrame::OnTraversalListenPortChanged, this); @@ -187,63 +242,35 @@ NetPlaySetupFrame::NetPlaySetupFrame(wxWindow* const parent, const CGameListCtrl new wxListBox(host_tab, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxLB_SORT); m_game_lbox->Bind(wxEVT_LISTBOX_DCLICK, &NetPlaySetupFrame::OnHost, this); - NetPlayDialog::FillWithGameNames(m_game_lbox, *game_list); - - std::string last_hosted_game; - if (netplay_section.Get("SelectedHostGame", &last_hosted_game, "")) - m_game_lbox->SetStringSelection(last_hosted_game); + NetPlayDialog::FillWithGameNames(m_game_lbox, *m_game_list); wxBoxSizer* const top_szr = new wxBoxSizer(wxHORIZONTAL); - top_szr->Add(m_host_port_lbl, 0, wxCENTER | wxRIGHT, 5); - top_szr->Add(m_host_port_text, 0); + top_szr->Add(m_host_port_lbl, 0, wxALIGN_CENTER_VERTICAL); + top_szr->Add(m_host_port_text, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); #ifdef USE_UPNP m_upnp_chk = new wxCheckBox(host_tab, wxID_ANY, _("Forward port (UPnP)")); - top_szr->Add(m_upnp_chk, 0, wxALL, 5); + top_szr->Add(m_upnp_chk, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); #endif + wxBoxSizer* const bottom_szr = new wxBoxSizer(wxHORIZONTAL); - bottom_szr->Add(m_traversal_listen_port_enabled, 0, wxCENTER | wxLEFT, 5); - bottom_szr->Add(m_traversal_listen_port, 0, wxCENTER, 0); - wxBoxSizer* const host_btn_szr = new wxBoxSizer(wxVERTICAL); - host_btn_szr->Add(host_btn, 0, wxCENTER | wxALIGN_RIGHT, 0); - bottom_szr->Add(host_btn_szr, 1, wxALL, 5); + bottom_szr->Add(m_traversal_listen_port_enabled, 0, wxALIGN_CENTER_VERTICAL); + bottom_szr->Add(m_traversal_listen_port, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); + bottom_szr->AddStretchSpacer(); + bottom_szr->Add(host_btn, 0, wxLEFT, space5); wxBoxSizer* const host_szr = new wxBoxSizer(wxVERTICAL); - host_szr->Add(top_szr, 0, wxALL | wxEXPAND, 5); - host_szr->Add(m_game_lbox, 1, wxLEFT | wxRIGHT | wxEXPAND, 5); - host_szr->Add(bottom_szr, 0, wxEXPAND, 0); + // NOTE: Top row can disappear entirely + host_szr->Add(top_szr, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, space5); + host_szr->AddSpacer(space5); + host_szr->Add(m_game_lbox, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + host_szr->AddSpacer(space5); + host_szr->Add(bottom_szr, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + host_szr->AddSpacer(space5); host_tab->SetSizerAndFit(host_szr); } - // bottom row - wxButton* const quit_btn = new wxButton(panel, wxID_ANY, _("Quit")); - quit_btn->Bind(wxEVT_BUTTON, &NetPlaySetupFrame::OnQuit, this); - - // main sizer - wxBoxSizer* const main_szr = new wxBoxSizer(wxVERTICAL); - main_szr->Add(trav_szr, 0, wxALL | wxALIGN_LEFT, 5); - main_szr->Add(nick_szr, 0, wxALL | wxALIGN_LEFT, 5); - main_szr->Add(m_traversal_lbl, 0, wxALL | wxALIGN_LEFT, 5); - main_szr->Add(m_notebook, 1, wxLEFT | wxRIGHT | wxEXPAND, 5); - main_szr->Add(quit_btn, 0, wxALL | wxALIGN_RIGHT, 5); - - panel->SetSizerAndFit(main_szr); - - // Handle focus on tab changes - panel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &NetPlaySetupFrame::OnTabChanged, this); - - // wxBoxSizer* const diag_szr = new wxBoxSizer(wxVERTICAL); - // diag_szr->Add(panel, 1, wxEXPAND); - // SetSizerAndFit(diag_szr); - - main_szr->SetSizeHints(this); - - Center(); - Show(); - - // Needs to be done last or it set up the spacing on the page correctly - wxCommandEvent ev; - OnDirectTraversalChoice(ev); + return notebook; } NetPlaySetupFrame::~NetPlaySetupFrame() @@ -278,6 +305,10 @@ NetPlaySetupFrame::~NetPlaySetupFrame() m_traversal_listen_port->GetValue() : 0); +#ifdef USE_UPNP + netplay_section.Set("UseUPNP", m_upnp_chk->GetValue(), false); +#endif + inifile.Save(dolphin_ini); main_frame->g_NetPlaySetupDiag = nullptr; } @@ -395,7 +426,7 @@ void NetPlaySetupFrame::OnDirectTraversalChoice(wxCommandEvent& event) if (sel == TRAVERSAL_CHOICE) { - m_traversal_lbl->Show(); + m_traversal_lbl->SetLabelText(m_traversal_string); m_trav_reset_btn->Show(); m_connect_hashcode_text->Show(); m_connect_ip_text->Hide(); @@ -420,7 +451,7 @@ void NetPlaySetupFrame::OnDirectTraversalChoice(wxCommandEvent& event) } else { - m_traversal_lbl->Hide(); + m_traversal_lbl->SetLabel(wxEmptyString); m_trav_reset_btn->Hide(); m_connect_hashcode_text->Hide(); m_connect_ip_text->Show(); @@ -446,7 +477,19 @@ void NetPlaySetupFrame::OnDirectTraversalChoice(wxCommandEvent& event) m_upnp_chk->Show(); #endif } - m_connect_ip_text->GetParent()->Layout(); + + // wxWidgets' layout engine sucks. It only updates when a size event occurs so we + // have to manually invoke the layout system. + // Caveat: This only works if the new layout is not substantially different from the + // old one because otherwise the minimum sizes assigned by SetSizerAndFit won't make + // sense and the layout will break (overlapping widgets). You can't just SetSizeHints + // because that will change the current sizes as well as the minimum sizes, it's a mess. + for (wxWindow* tab : m_notebook->GetChildren()) + tab->Layout(); + // Because this is a wxFrame, not a dialog, everything is inside a wxPanel which + // is the only direct child of the frame. + GetChildren()[0]->Layout(); + DispatchFocus(); } @@ -476,16 +519,8 @@ void NetPlaySetupFrame::OnTabChanged(wxCommandEvent& event) // Propagate event event.Skip(); - // Delaying action so the first tab order element doesn't override the focus - m_notebook->Bind(wxEVT_IDLE, &NetPlaySetupFrame::OnAfterTabChange, this); -} - -void NetPlaySetupFrame::OnAfterTabChange(wxIdleEvent&) -{ - // Unbinding so we don't hog the idle event - m_notebook->Unbind(wxEVT_IDLE, &NetPlaySetupFrame::OnAfterTabChange, this); - - DispatchFocus(); + // Let the base class fiddle with the focus first then correct it afterwards + CallAfter(&NetPlaySetupFrame::DispatchFocus); } void NetPlaySetupFrame::DispatchFocus() diff --git a/Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.h b/Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.h index be565b1452..a91e26989b 100644 --- a/Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.h +++ b/Source/Core/DolphinWX/NetPlay/NetPlaySetupFrame.h @@ -12,6 +12,7 @@ class CGameListCtrl; class wxCheckBox; class wxChoice; class wxListBox; +class wxNotebook; class wxSpinCtrl; class wxStaticText; class wxTextCtrl; @@ -29,7 +30,9 @@ private: static constexpr int DIRECT_CHOICE = 0; static constexpr int TRAVERSAL_CHOICE = 1; - void GetWindowRect(const IniFile::Section& section, wxRect* rect) const; + void CreateGUI(); + wxNotebook* CreateNotebookGUI(wxWindow* parent); + void OnJoin(wxCommandEvent& event); void OnHost(wxCommandEvent& event); void DoJoin(); @@ -40,7 +43,6 @@ private: void OnTraversalListenPortChanged(wxCommandEvent& event); void OnKeyDown(wxKeyEvent& event); void OnTabChanged(wxCommandEvent& event); - void OnAfterTabChange(wxIdleEvent& event); void DispatchFocus(); wxStaticText* m_ip_lbl; @@ -63,5 +65,6 @@ private: wxCheckBox* m_upnp_chk; #endif + wxString m_traversal_string; const CGameListCtrl* const m_game_list; }; diff --git a/Source/Core/DolphinWX/NetPlay/NetWindow.cpp b/Source/Core/DolphinWX/NetPlay/NetWindow.cpp index 18dabd91e2..90f60e3c1b 100644 --- a/Source/Core/DolphinWX/NetPlay/NetWindow.cpp +++ b/Source/Core/DolphinWX/NetPlay/NetWindow.cpp @@ -4,6 +4,7 @@ #include <algorithm> #include <cstddef> +#include <limits> #include <sstream> #include <string> #include <tuple> @@ -21,6 +22,7 @@ #include <wx/panel.h> #include <wx/sizer.h> #include <wx/spinctrl.h> +#include <wx/statbox.h> #include <wx/stattext.h> #include <wx/string.h> #include <wx/textctrl.h> @@ -67,12 +69,52 @@ NetPlayDialog::NetPlayDialog(wxWindow* const parent, const CGameListCtrl* const m_host_copy_btn_is_retry(false), m_is_hosting(is_hosting), m_game_list(game_list) { Bind(wxEVT_THREAD, &NetPlayDialog::OnThread, this); + CreateGUI(); + SetIcons(WxUtils::GetDolphinIconBundle()); + Center(); + // Remember the window size and position for NetWindow + { + IniFile inifile; + inifile.Load(File::GetUserPath(F_DOLPHINCONFIG_IDX)); + IniFile::Section& netplay_section = *inifile.GetOrCreateSection("NetPlay"); + + int winPosX, winPosY, winWidth, winHeight; + netplay_section.Get("NetWindowPosX", &winPosX, std::numeric_limits<int>::min()); + netplay_section.Get("NetWindowPosY", &winPosY, std::numeric_limits<int>::min()); + netplay_section.Get("NetWindowWidth", &winWidth, -1); + netplay_section.Get("NetWindowHeight", &winHeight, -1); + + WxUtils::SetWindowSizeAndFitToScreen(this, wxPoint(winPosX, winPosY), + wxSize(winWidth, winHeight), GetSize()); + } +} + +void NetPlayDialog::CreateGUI() +{ + const int space5 = FromDIP(5); + + // NOTE: The design operates top down. Margins / padding are handled by the outermost + // sizers, this makes the design easier to change. Inner sizers should only pad between + // widgets, they should never have prepended or appended margins/spacers. wxPanel* const panel = new wxPanel(this); + wxBoxSizer* const main_szr = new wxBoxSizer(wxVERTICAL); + main_szr->AddSpacer(space5); + main_szr->Add(CreateTopGUI(panel), 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_szr->AddSpacer(space5); + main_szr->Add(CreateMiddleGUI(panel), 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_szr->AddSpacer(space5); + main_szr->Add(CreateBottomGUI(panel), 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + main_szr->AddSpacer(space5); - wxBoxSizer* const top_szr = new wxBoxSizer(wxHORIZONTAL); + panel->SetSizerAndFit(main_szr); + main_szr->SetSizeHints(this); + SetSize(FromDIP(wxSize(768, 768 - 128))); +} - m_game_btn = new wxButton(panel, wxID_ANY, StrToWxStr(m_selected_game).Prepend(_(" Game : ")), +wxSizer* NetPlayDialog::CreateTopGUI(wxWindow* parent) +{ + m_game_btn = new wxButton(parent, wxID_ANY, _(" Game : ") + StrToWxStr(m_selected_game), wxDefaultPosition, wxDefaultSize, wxBU_LEFT); if (m_is_hosting) @@ -80,11 +122,12 @@ NetPlayDialog::NetPlayDialog(wxWindow* const parent, const CGameListCtrl* const else m_game_btn->Disable(); - top_szr->Add(m_game_btn, 1, wxALL | wxEXPAND); + wxBoxSizer* top_szr = new wxBoxSizer(wxHORIZONTAL); + top_szr->Add(m_game_btn, 1, wxEXPAND); if (m_is_hosting) { - m_MD5_choice = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxSize(150, -1)); + m_MD5_choice = new wxChoice(parent, wxID_ANY, wxDefaultPosition, FromDIP(wxSize(150, -1))); m_MD5_choice->Bind(wxEVT_CHOICE, &NetPlayDialog::OnMD5ComputeRequested, this); m_MD5_choice->Append(_("MD5 check...")); m_MD5_choice->Append(_("Current game")); @@ -92,59 +135,89 @@ NetPlayDialog::NetPlayDialog(wxWindow* const parent, const CGameListCtrl* const m_MD5_choice->Append(_("SD card")); m_MD5_choice->SetSelection(0); - top_szr->Add(m_MD5_choice, 0, wxALL); + top_szr->Add(m_MD5_choice, 0, wxALIGN_CENTER_VERTICAL); } - // middle crap + return top_szr; +} - // chat - m_chat_text = new wxTextCtrl(panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, +wxSizer* NetPlayDialog::CreateMiddleGUI(wxWindow* parent) +{ + const int space5 = FromDIP(5); + + wxBoxSizer* const mid_szr = new wxBoxSizer(wxHORIZONTAL); + mid_szr->Add(CreateChatGUI(parent), 1, wxEXPAND); + mid_szr->Add(CreatePlayerListGUI(parent), 0, wxEXPAND | wxLEFT, space5); + return mid_szr; +} + +wxSizer* NetPlayDialog::CreateChatGUI(wxWindow* parent) +{ + const int space5 = FromDIP(5); + + wxStaticBoxSizer* const chat_szr = new wxStaticBoxSizer(wxVERTICAL, parent, _("Chat")); + parent = chat_szr->GetStaticBox(); + + m_chat_text = new wxTextCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxTE_MULTILINE); - m_chat_msg_text = new wxTextCtrl(panel, wxID_ANY, wxEmptyString, wxDefaultPosition, - wxSize(-1, 25), wxTE_PROCESS_ENTER); + m_chat_msg_text = new wxTextCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, + wxDefaultSize, wxTE_PROCESS_ENTER); m_chat_msg_text->Bind(wxEVT_TEXT_ENTER, &NetPlayDialog::OnChat, this); m_chat_msg_text->SetMaxLength(2000); wxButton* const chat_msg_btn = - new wxButton(panel, wxID_ANY, _("Send"), wxDefaultPosition, wxSize(-1, 26)); + new wxButton(parent, wxID_ANY, _("Send"), wxDefaultPosition, + wxSize(-1, m_chat_msg_text->GetBestSize().GetHeight())); chat_msg_btn->Bind(wxEVT_BUTTON, &NetPlayDialog::OnChat, this); wxBoxSizer* const chat_msg_szr = new wxBoxSizer(wxHORIZONTAL); - chat_msg_szr->Add(m_chat_msg_text, 1); - chat_msg_szr->Add(chat_msg_btn, 0); + // NOTE: Remember that fonts are configurable, setting sizes of anything that contains + // text in pixels is dangerous because the text may end up being clipped. + chat_msg_szr->Add(WxUtils::GiveMinSizeDIP(m_chat_msg_text, wxSize(-1, 25)), 1, + wxALIGN_CENTER_VERTICAL); + chat_msg_szr->Add(chat_msg_btn, 0, wxEXPAND); - wxStaticBoxSizer* const chat_szr = new wxStaticBoxSizer(wxVERTICAL, panel, _("Chat")); chat_szr->Add(m_chat_text, 1, wxEXPAND); - chat_szr->Add(chat_msg_szr, 0, wxEXPAND | wxTOP, 5); + chat_szr->Add(chat_msg_szr, 0, wxEXPAND | wxTOP, space5); + return chat_szr; +} - m_player_lbox = new wxListBox(panel, wxID_ANY, wxDefaultPosition, wxSize(256, -1)); +wxSizer* NetPlayDialog::CreatePlayerListGUI(wxWindow* parent) +{ + const int space5 = FromDIP(5); - wxStaticBoxSizer* const player_szr = new wxStaticBoxSizer(wxVERTICAL, panel, _("Players")); + wxStaticBoxSizer* const player_szr = new wxStaticBoxSizer(wxVERTICAL, parent, _("Players")); + // Static box is a widget, new widgets should be children instead of siblings to avoid various + // flickering problems. + parent = player_szr->GetStaticBox(); + + m_player_lbox = new wxListBox(parent, wxID_ANY, wxDefaultPosition, FromDIP(wxSize(256, -1)), 0, + nullptr, wxLB_HSCROLL); - // player list if (m_is_hosting && g_TraversalClient) { - wxBoxSizer* const host_szr = new wxBoxSizer(wxHORIZONTAL); - m_host_type_choice = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxSize(76, -1)); + m_host_type_choice = new wxChoice(parent, wxID_ANY, wxDefaultPosition, FromDIP(wxSize(76, -1))); m_host_type_choice->Bind(wxEVT_CHOICE, &NetPlayDialog::OnChoice, this); m_host_type_choice->Append(_("Room ID:")); - host_szr->Add(m_host_type_choice); - - m_host_label = new wxStaticText(panel, wxID_ANY, "555.555.555.555:55555", wxDefaultPosition, - wxDefaultSize, wxST_NO_AUTORESIZE | wxALIGN_LEFT); - // Update() should fix this immediately. - m_host_label->SetLabel(""); - host_szr->Add(m_host_label, 1, wxLEFT | wxCENTER, 5); - - m_host_copy_btn = new wxButton(panel, wxID_ANY, _("Copy")); - m_host_copy_btn->Bind(wxEVT_BUTTON, &NetPlayDialog::OnCopyIP, this); - m_host_copy_btn->Disable(); - host_szr->Add(m_host_copy_btn, 0, wxLEFT | wxCENTER, 5); - player_szr->Add(host_szr, 0, wxEXPAND | wxBOTTOM, 5); m_host_type_choice->Select(0); + m_host_label = new wxStaticText(parent, wxID_ANY, "555.555.555.555:55555", wxDefaultPosition, + wxDefaultSize, wxST_NO_AUTORESIZE); + // Update() should fix this immediately. + m_host_label->SetLabel(""); + + m_host_copy_btn = new wxButton(parent, wxID_ANY, _("Copy")); + m_host_copy_btn->Bind(wxEVT_BUTTON, &NetPlayDialog::OnCopyIP, this); + m_host_copy_btn->Disable(); + UpdateHostLabel(); + + wxBoxSizer* const host_szr = new wxBoxSizer(wxHORIZONTAL); + host_szr->Add(m_host_type_choice); + host_szr->Add(m_host_label, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); + host_szr->Add(m_host_copy_btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); + player_szr->Add(host_szr, 0, wxEXPAND, space5); } player_szr->Add(m_player_lbox, 1, wxEXPAND); @@ -152,59 +225,54 @@ NetPlayDialog::NetPlayDialog(wxWindow* const parent, const CGameListCtrl* const if (m_is_hosting) { m_player_lbox->Bind(wxEVT_LISTBOX, &NetPlayDialog::OnPlayerSelect, this); - m_kick_btn = new wxButton(panel, wxID_ANY, _("Kick Player")); + m_kick_btn = new wxButton(parent, wxID_ANY, _("Kick Player")); m_kick_btn->Bind(wxEVT_BUTTON, &NetPlayDialog::OnKick, this); - player_szr->Add(m_kick_btn, 0, wxEXPAND | wxTOP, 5); m_kick_btn->Disable(); - m_player_config_btn = new wxButton(panel, wxID_ANY, _("Assign Controller Ports")); + m_player_config_btn = new wxButton(parent, wxID_ANY, _("Assign Controller Ports")); m_player_config_btn->Bind(wxEVT_BUTTON, &NetPlayDialog::OnAssignPads, this); - player_szr->Add(m_player_config_btn, 0, wxEXPAND | wxTOP, 5); + + player_szr->Add(m_kick_btn, 0, wxEXPAND | wxTOP, space5); + player_szr->Add(m_player_config_btn, 0, wxEXPAND | wxTOP, space5); } + return player_szr; +} - wxBoxSizer* const mid_szr = new wxBoxSizer(wxHORIZONTAL); - mid_szr->Add(chat_szr, 1, wxEXPAND | wxRIGHT, 5); - mid_szr->Add(player_szr, 0, wxEXPAND); - - // bottom crap - wxButton* const quit_btn = new wxButton(panel, wxID_ANY, _("Quit Netplay")); - quit_btn->Bind(wxEVT_BUTTON, &NetPlayDialog::OnQuit, this); +wxSizer* NetPlayDialog::CreateBottomGUI(wxWindow* parent) +{ + const int space5 = FromDIP(5); wxBoxSizer* const bottom_szr = new wxBoxSizer(wxHORIZONTAL); - if (is_hosting) + if (m_is_hosting) { - m_start_btn = new wxButton(panel, wxID_ANY, _("Start")); + m_start_btn = new wxButton(parent, wxID_ANY, _("Start")); m_start_btn->Bind(wxEVT_BUTTON, &NetPlayDialog::OnStart, this); - bottom_szr->Add(m_start_btn); - bottom_szr->Add(new wxStaticText(panel, wxID_ANY, _("Buffer:")), 0, wxLEFT | wxCENTER, 5); + wxStaticText* buffer_lbl = new wxStaticText(parent, wxID_ANY, _("Buffer:")); wxSpinCtrl* const padbuf_spin = - new wxSpinCtrl(panel, wxID_ANY, std::to_string(INITIAL_PAD_BUFFER_SIZE), wxDefaultPosition, - wxSize(64, -1), wxSP_ARROW_KEYS, 0, 200, INITIAL_PAD_BUFFER_SIZE); + new wxSpinCtrl(parent, wxID_ANY, std::to_string(INITIAL_PAD_BUFFER_SIZE), wxDefaultPosition, + wxDefaultSize, wxSP_ARROW_KEYS, 0, 200, INITIAL_PAD_BUFFER_SIZE); padbuf_spin->Bind(wxEVT_SPINCTRL, &NetPlayDialog::OnAdjustBuffer, this); - bottom_szr->AddSpacer(3); - bottom_szr->Add(padbuf_spin, 0, wxCENTER); - bottom_szr->AddSpacer(5); - m_memcard_write = new wxCheckBox(panel, wxID_ANY, _("Write to memcards/SD")); - bottom_szr->Add(m_memcard_write, 0, wxCENTER); + padbuf_spin->SetMinSize(WxUtils::GetTextWidgetMinSize(padbuf_spin)); + + m_memcard_write = new wxCheckBox(parent, wxID_ANY, _("Write to memcards/SD")); + + bottom_szr->Add(m_start_btn, 0, wxALIGN_CENTER_VERTICAL); + bottom_szr->Add(buffer_lbl, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); + bottom_szr->Add(padbuf_spin, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); + bottom_szr->Add(m_memcard_write, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); + bottom_szr->AddSpacer(space5); } - bottom_szr->AddSpacer(5); - m_record_chkbox = new wxCheckBox(panel, wxID_ANY, _("Record inputs")); - bottom_szr->Add(m_record_chkbox, 0, wxCENTER); + m_record_chkbox = new wxCheckBox(parent, wxID_ANY, _("Record inputs")); - bottom_szr->AddStretchSpacer(1); + wxButton* quit_btn = new wxButton(parent, wxID_ANY, _("Quit Netplay")); + quit_btn->Bind(wxEVT_BUTTON, &NetPlayDialog::OnQuit, this); + + bottom_szr->Add(m_record_chkbox, 0, wxALIGN_CENTER_VERTICAL); + bottom_szr->AddStretchSpacer(); bottom_szr->Add(quit_btn); - - // main sizer - wxBoxSizer* const main_szr = new wxBoxSizer(wxVERTICAL); - main_szr->Add(top_szr, 0, wxEXPAND | wxALL, 5); - main_szr->Add(mid_szr, 1, wxEXPAND | wxLEFT | wxRIGHT, 5); - main_szr->Add(bottom_szr, 0, wxEXPAND | wxALL, 5); - - panel->SetSizerAndFit(main_szr); - - main_szr->SetSizeHints(this); + return bottom_szr; } NetPlayDialog::~NetPlayDialog() diff --git a/Source/Core/DolphinWX/NetPlay/NetWindow.h b/Source/Core/DolphinWX/NetPlay/NetWindow.h index f6623a8a1c..1dd36c52e4 100644 --- a/Source/Core/DolphinWX/NetPlay/NetWindow.h +++ b/Source/Core/DolphinWX/NetPlay/NetWindow.h @@ -19,6 +19,7 @@ class wxButton; class wxCheckBox; class wxChoice; class wxListBox; +class wxSizer; class wxStaticText; class wxString; class wxTextCtrl; @@ -101,6 +102,13 @@ public: bool IsRecording() override; private: + void CreateGUI(); + wxSizer* CreateTopGUI(wxWindow* parent); + wxSizer* CreateMiddleGUI(wxWindow* parent); + wxSizer* CreateChatGUI(wxWindow* parent); + wxSizer* CreatePlayerListGUI(wxWindow* parent); + wxSizer* CreateBottomGUI(wxWindow* parent); + void OnChat(wxCommandEvent& event); void OnQuit(wxCommandEvent& event); void OnThread(wxThreadEvent& event); @@ -133,7 +141,7 @@ private: wxStaticText* m_host_label; wxChoice* m_host_type_choice; wxButton* m_host_copy_btn; - wxChoice* m_MD5_choice; + wxChoice* m_MD5_choice = nullptr; MD5Dialog* m_MD5_dialog = nullptr; bool m_host_copy_btn_is_retry; bool m_is_hosting; diff --git a/Source/Core/DolphinWX/NetPlay/PadMapDialog.cpp b/Source/Core/DolphinWX/NetPlay/PadMapDialog.cpp index b5e5badc7c..d2bef9a687 100644 --- a/Source/Core/DolphinWX/NetPlay/PadMapDialog.cpp +++ b/Source/Core/DolphinWX/NetPlay/PadMapDialog.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <wx/choice.h> +#include <wx/gbsizer.h> #include <wx/sizer.h> #include <wx/stattext.h> @@ -10,79 +11,63 @@ #include "Core/NetPlayProto.h" #include "Core/NetPlayServer.h" #include "DolphinWX/NetPlay/PadMapDialog.h" +#include "DolphinWX/WxUtils.h" PadMapDialog::PadMapDialog(wxWindow* parent, NetPlayServer* server, NetPlayClient* client) : wxDialog(parent, wxID_ANY, _("Controller Ports")), m_pad_mapping(server->GetPadMapping()), m_wii_mapping(server->GetWiimoteMapping()), m_player_list(client->GetPlayers()) { - wxBoxSizer* const h_szr = new wxBoxSizer(wxHORIZONTAL); - h_szr->AddSpacer(10); + const int space5 = FromDIP(5); + const int space10 = FromDIP(10); + + wxGridBagSizer* pad_sizer = new wxGridBagSizer(space5, space10); wxArrayString player_names; player_names.Add(_("None")); - for (auto& player : m_player_list) - player_names.Add(player->name); + for (const auto& player : m_player_list) + player_names.Add(StrToWxStr(player->name)); - for (unsigned int i = 0; i < 4; ++i) - { - wxBoxSizer* const v_szr = new wxBoxSizer(wxVERTICAL); - v_szr->Add(new wxStaticText(this, wxID_ANY, (wxString(_("GC Port ")) + (wxChar)('1' + i))), 1, - wxALIGN_CENTER_HORIZONTAL); + auto build_choice = [&](unsigned int base_idx, unsigned int idx, const PadMappingArray& mapping, + const wxString& port_name) { + pad_sizer->Add(new wxStaticText(this, wxID_ANY, wxString::Format("%s %d", port_name, idx + 1)), + wxGBPosition(0, base_idx + idx), wxDefaultSpan, wxALIGN_CENTER); - m_map_cbox[i] = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, player_names); - m_map_cbox[i]->Bind(wxEVT_CHOICE, &PadMapDialog::OnAdjust, this); - if (m_pad_mapping[i] == -1) - { - m_map_cbox[i]->Select(0); - } - else - { - for (unsigned int j = 0; j < m_player_list.size(); j++) - { - if (m_pad_mapping[i] == m_player_list[j]->pid) - m_map_cbox[i]->Select(j + 1); - } - } - - v_szr->Add(m_map_cbox[i], 1); - - h_szr->Add(v_szr, 1, wxTOP | wxEXPAND, 20); - h_szr->AddSpacer(10); - } - - for (unsigned int i = 0; i < 4; ++i) - { - wxBoxSizer* const v_szr = new wxBoxSizer(wxVERTICAL); - v_szr->Add(new wxStaticText(this, wxID_ANY, (wxString(_("Wiimote ")) + (wxChar)('1' + i))), 1, - wxALIGN_CENTER_HORIZONTAL); - - m_map_cbox[i + 4] = + m_map_cbox[base_idx + idx] = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, player_names); - m_map_cbox[i + 4]->Bind(wxEVT_CHOICE, &PadMapDialog::OnAdjust, this); - if (m_wii_mapping[i] == -1) - { - m_map_cbox[i + 4]->Select(0); - } - else + m_map_cbox[base_idx + idx]->Select(0); + m_map_cbox[base_idx + idx]->Bind(wxEVT_CHOICE, &PadMapDialog::OnAdjust, this); + if (mapping[idx] != -1) { for (unsigned int j = 0; j < m_player_list.size(); j++) { - if (m_wii_mapping[i] == m_player_list[j]->pid) - m_map_cbox[i + 4]->Select(j + 1); + if (m_pad_mapping[idx] == m_player_list[j]->pid) + { + m_map_cbox[base_idx + idx]->Select(j + 1); + break; + } } } + // Combo boxes break on Windows when wxEXPAND-ed vertically but you can't control the + // direction of expansion in a grid sizer. Solution is to wrap in a box sizer. + wxBoxSizer* wrapper = new wxBoxSizer(wxHORIZONTAL); + wrapper->Add(m_map_cbox[base_idx + idx], 1, wxALIGN_CENTER_VERTICAL); + pad_sizer->Add(wrapper, wxGBPosition(1, base_idx + idx), wxDefaultSpan, wxEXPAND); + }; - v_szr->Add(m_map_cbox[i + 4], 1); - - h_szr->Add(v_szr, 1, wxTOP | wxEXPAND, 20); - h_szr->AddSpacer(10); + for (unsigned int i = 0; i < 4; ++i) + { + // This looks a little weird but it's fine because we're using a grid bag sizer; + // we can add columns in any order. + build_choice(0, i, m_pad_mapping, _("GC Port")); + build_choice(4, i, m_wii_mapping, _("Wiimote")); } wxBoxSizer* const main_szr = new wxBoxSizer(wxVERTICAL); - main_szr->Add(h_szr); - main_szr->AddSpacer(5); - main_szr->Add(CreateButtonSizer(wxOK), 0, wxEXPAND | wxLEFT | wxRIGHT, 20); - main_szr->AddSpacer(5); + main_szr->AddSpacer(space10); + main_szr->Add(pad_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space10); + main_szr->AddSpacer(space10); + main_szr->Add(CreateButtonSizer(wxOK), 0, wxEXPAND | wxLEFT | wxRIGHT, space10); + main_szr->AddSpacer(space5); SetSizerAndFit(main_szr); SetFocus(); } diff --git a/Source/Core/DolphinWX/NetPlay/PadMapDialog.h b/Source/Core/DolphinWX/NetPlay/PadMapDialog.h index 30d0266555..d8f4167eb6 100644 --- a/Source/Core/DolphinWX/NetPlay/PadMapDialog.h +++ b/Source/Core/DolphinWX/NetPlay/PadMapDialog.h @@ -12,7 +12,9 @@ class NetPlayClient; class NetPlayServer; class Player; +class wxArrayString; class wxChoice; +class wxSizer; class PadMapDialog final : public wxDialog { diff --git a/Source/Core/DolphinWX/PatchAddEdit.cpp b/Source/Core/DolphinWX/PatchAddEdit.cpp index a845c60016..e58b90a913 100644 --- a/Source/Core/DolphinWX/PatchAddEdit.cpp +++ b/Source/Core/DolphinWX/PatchAddEdit.cpp @@ -52,7 +52,8 @@ void CPatchAddEdit::CreateGUIControls(int _selection) itCurEntry = tempEntries.begin(); - wxBoxSizer* sEditPatch = new wxBoxSizer(wxVERTICAL); + const int space5 = FromDIP(5); + const int space10 = FromDIP(10); wxStaticText* EditPatchNameText = new wxStaticText(this, wxID_ANY, _("Name:")); EditPatchName = new wxTextCtrl(this, wxID_ANY); @@ -89,32 +90,38 @@ void CPatchAddEdit::CreateGUIControls(int _selection) EntryRemove->Disable(); wxBoxSizer* sEditPatchName = new wxBoxSizer(wxHORIZONTAL); - sEditPatchName->Add(EditPatchNameText, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - sEditPatchName->Add(EditPatchName, 1, wxEXPAND | wxALL, 5); - sEditPatch->Add(sEditPatchName, 0, wxEXPAND); + sEditPatchName->Add(EditPatchNameText, 0, wxALIGN_CENTER_VERTICAL); + sEditPatchName->Add(EditPatchName, 1, wxEXPAND | wxLEFT, space5); sbEntry = new wxStaticBoxSizer(wxVERTICAL, this, wxString::Format(_("Entry 1/%d"), (int)tempEntries.size())); currentItem = 1; - wxGridBagSizer* sgEntry = new wxGridBagSizer(0, 0); - sgEntry->Add(EditPatchType, wxGBPosition(0, 0), wxGBSpan(1, 2), wxEXPAND | wxALL, 5); - sgEntry->Add(EditPatchOffsetText, wxGBPosition(1, 0), wxGBSpan(1, 1), - wxALIGN_CENTER_VERTICAL | wxALL, 5); - sgEntry->Add(EditPatchOffset, wxGBPosition(1, 1), wxGBSpan(1, 1), wxEXPAND | wxALL, 5); - sgEntry->Add(EditPatchValueText, wxGBPosition(2, 0), wxGBSpan(1, 1), - wxALIGN_CENTER_VERTICAL | wxALL, 5); - sgEntry->Add(EditPatchValue, wxGBPosition(2, 1), wxGBSpan(1, 1), wxEXPAND | wxALL, 5); - sgEntry->Add(EntrySelection, wxGBPosition(0, 2), wxGBSpan(3, 1), wxEXPAND | wxALL, 5); + wxGridBagSizer* sgEntry = new wxGridBagSizer(space10, space10); + sgEntry->Add(EditPatchType, wxGBPosition(0, 0), wxGBSpan(1, 2), wxEXPAND); + sgEntry->Add(EditPatchOffsetText, wxGBPosition(1, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL); + sgEntry->Add(EditPatchOffset, wxGBPosition(1, 1), wxGBSpan(1, 1), wxEXPAND); + sgEntry->Add(EditPatchValueText, wxGBPosition(2, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL); + sgEntry->Add(EditPatchValue, wxGBPosition(2, 1), wxGBSpan(1, 1), wxEXPAND); + sgEntry->Add(EntrySelection, wxGBPosition(0, 2), wxGBSpan(3, 1), wxEXPAND); sgEntry->AddGrowableCol(1); wxBoxSizer* sEntryAddRemove = new wxBoxSizer(wxHORIZONTAL); - sEntryAddRemove->Add(EntryAdd, 0, wxALL, 5); - sEntryAddRemove->Add(EntryRemove, 0, wxALL, 5); - sbEntry->Add(sgEntry, 0, wxEXPAND); - sbEntry->Add(sEntryAddRemove, 0, wxEXPAND); + sEntryAddRemove->Add(EntryAdd, 0, wxALIGN_CENTER_VERTICAL); + sEntryAddRemove->Add(EntryRemove, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); + sbEntry->AddSpacer(space5); + sbEntry->Add(sgEntry, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sbEntry->AddSpacer(space5); + sbEntry->Add(sEntryAddRemove, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sbEntry->AddSpacer(space5); - sEditPatch->Add(sbEntry, 0, wxEXPAND | wxALL, 5); - sEditPatch->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + wxBoxSizer* sEditPatch = new wxBoxSizer(wxVERTICAL); + sEditPatch->AddSpacer(space5); + sEditPatch->Add(sEditPatchName, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sEditPatch->AddSpacer(space5); + sEditPatch->Add(sbEntry, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sEditPatch->AddSpacer(space5); + sEditPatch->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sEditPatch->AddSpacer(space5); SetSizerAndFit(sEditPatch); SetFocus(); } diff --git a/Source/Core/DolphinWX/PostProcessingConfigDiag.cpp b/Source/Core/DolphinWX/PostProcessingConfigDiag.cpp index 19c4a2b952..019bdbe887 100644 --- a/Source/Core/DolphinWX/PostProcessingConfigDiag.cpp +++ b/Source/Core/DolphinWX/PostProcessingConfigDiag.cpp @@ -2,15 +2,13 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include <math.h> -#include <unordered_map> +#include <cmath> #include <wx/button.h> #include <wx/checkbox.h> #include <wx/notebook.h> #include <wx/panel.h> #include <wx/sizer.h> -#include <wx/slider.h> #include <wx/stattext.h> #include <wx/textctrl.h> @@ -37,45 +35,54 @@ PostProcessingConfigDiag::PostProcessingConfigDiag(wxWindow* parent, const std:: // Create our UI classes const PostProcessingShaderConfiguration::ConfigMap& config_map = m_post_processor->GetOptions(); + std::vector<std::unique_ptr<ConfigGrouping>> config_groups; + config_groups.reserve(config_map.size()); + m_config_map.reserve(config_map.size()); for (const auto& it : config_map) { + std::unique_ptr<ConfigGrouping> group; if (it.second.m_type == PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_BOOL) { - ConfigGrouping* group = - new ConfigGrouping(ConfigGrouping::WidgetType::TYPE_TOGGLE, it.second.m_gui_name, - it.first, it.second.m_dependent_option, &it.second); - m_config_map[it.first] = group; + group = std::make_unique<ConfigGrouping>(ConfigGrouping::WidgetType::TYPE_TOGGLE, + it.second.m_gui_name, it.first, + it.second.m_dependent_option, &it.second); } else { - ConfigGrouping* group = - new ConfigGrouping(ConfigGrouping::WidgetType::TYPE_SLIDER, it.second.m_gui_name, - it.first, it.second.m_dependent_option, &it.second); - m_config_map[it.first] = group; + group = std::make_unique<ConfigGrouping>(ConfigGrouping::WidgetType::TYPE_SLIDER, + it.second.m_gui_name, it.first, + it.second.m_dependent_option, &it.second); } + m_config_map[it.first] = group.get(); + config_groups.emplace_back(std::move(group)); } // Arrange our vectors based on dependency - for (const auto& it : m_config_map) + for (auto& group : config_groups) { - const std::string parent_name = it.second->GetParent(); - if (parent_name.size()) + const std::string& parent_name = group->GetParent(); + if (parent_name.empty()) { - // Since it depends on a different object, push it to a parent's object - m_config_map[parent_name]->AddChild(m_config_map[it.first]); + // It doesn't have a parent, just push it to the vector + m_config_groups.emplace_back(std::move(group)); } else { - // It doesn't have a child, just push it to the vector - m_config_groups.push_back(m_config_map[it.first]); + // Since it depends on a different object, push it to a parent's object + m_config_map[parent_name]->AddChild(std::move(group)); } } + config_groups.clear(); // Full of null unique_ptrs now + config_groups.shrink_to_fit(); + + const int space5 = FromDIP(5); + const int space10 = FromDIP(10); // Generate our UI wxNotebook* const notebook = new wxNotebook(this, wxID_ANY); wxPanel* const page_general = new wxPanel(notebook); - wxFlexGridSizer* const szr_general = new wxFlexGridSizer(2, 5, 5); + wxFlexGridSizer* const szr_general = new wxFlexGridSizer(2, space5, space5); // Now let's actually populate our window with our information bool add_general_page = false; @@ -85,7 +92,8 @@ PostProcessingConfigDiag::PostProcessingConfigDiag(wxWindow* parent, const std:: { // Options with children get their own tab wxPanel* const page_option = new wxPanel(notebook); - wxFlexGridSizer* const szr_option = new wxFlexGridSizer(2, 10, 5); + wxBoxSizer* const wrap_sizer = new wxBoxSizer(wxVERTICAL); + wxFlexGridSizer* const szr_option = new wxFlexGridSizer(2, space10, space5); it->GenerateUI(this, page_option, szr_option); // Add all the children @@ -93,8 +101,11 @@ PostProcessingConfigDiag::PostProcessingConfigDiag(wxWindow* parent, const std:: { child->GenerateUI(this, page_option, szr_option); } - page_option->SetSizerAndFit(szr_option); - notebook->AddPage(page_option, _(it->GetGUIName())); + wrap_sizer->AddSpacer(space5); + wrap_sizer->Add(szr_option, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + wrap_sizer->AddSpacer(space5); + page_option->SetSizerAndFit(wrap_sizer); + notebook->AddPage(page_option, it->GetGUIName()); } else { @@ -110,20 +121,30 @@ PostProcessingConfigDiag::PostProcessingConfigDiag(wxWindow* parent, const std:: if (add_general_page) { - page_general->SetSizerAndFit(szr_general); + wxBoxSizer* const wrap_sizer = new wxBoxSizer(wxVERTICAL); + wrap_sizer->AddSpacer(space5); + wrap_sizer->Add(szr_general, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + wrap_sizer->AddSpacer(space5); + + page_general->SetSizerAndFit(wrap_sizer); notebook->InsertPage(0, page_general, _("General")); } // Close Button - wxButton* const btn_close = new wxButton(this, wxID_OK, _("Close")); - btn_close->Bind(wxEVT_BUTTON, &PostProcessingConfigDiag::Event_ClickClose, this); - - Bind(wxEVT_CLOSE_WINDOW, &PostProcessingConfigDiag::Event_Close, this); + wxStdDialogButtonSizer* const btn_strip = CreateStdDialogButtonSizer(wxOK | wxNO_DEFAULT); + btn_strip->GetAffirmativeButton()->SetLabel(_("Close")); + SetEscapeId(wxID_OK); // Treat closing the window by 'X' or hitting escape as 'OK' wxBoxSizer* const szr_main = new wxBoxSizer(wxVERTICAL); - szr_main->Add(notebook, 1, wxEXPAND | wxALL, 5); - szr_main->Add(btn_close, 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 5); + szr_main->AddSpacer(space5); + szr_main->Add(notebook, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + szr_main->AddSpacer(space5); + szr_main->Add(btn_strip, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + szr_main->AddSpacer(space5); + szr_main->SetMinSize(FromDIP(wxSize(400, -1))); + SetLayoutAdaptationMode(wxDIALOG_ADAPTATION_MODE_ENABLED); + SetLayoutAdaptationLevel(wxDIALOG_ADAPTATION_STANDARD_SIZER); SetSizerAndFit(szr_main); Center(); SetFocus(); @@ -145,7 +166,7 @@ void PostProcessingConfigDiag::ConfigGrouping::GenerateUI(PostProcessingConfigDi { if (m_type == WidgetType::TYPE_TOGGLE) { - m_option_checkbox = new wxCheckBox(parent, wxID_ANY, _(m_gui_name)); + m_option_checkbox = new wxCheckBox(parent, wxID_ANY, m_gui_name); m_option_checkbox->SetValue(m_config_option->m_bool_value); m_option_checkbox->Bind(wxEVT_CHECKBOX, &PostProcessingConfigDiag::Event_CheckBox, dialog, wxID_ANY, wxID_ANY, new UserEventData(m_option)); @@ -162,8 +183,8 @@ void PostProcessingConfigDiag::ConfigGrouping::GenerateUI(PostProcessingConfigDi else vector_size = m_config_option->m_float_values.size(); - wxFlexGridSizer* const szr_values = new wxFlexGridSizer(vector_size + 1, 0, 0); - wxStaticText* const option_static_text = new wxStaticText(parent, wxID_ANY, _(m_gui_name)); + wxFlexGridSizer* const szr_values = new wxFlexGridSizer(vector_size + 1); + wxStaticText* const option_static_text = new wxStaticText(parent, wxID_ANY, m_gui_name); sizer->Add(option_static_text); for (size_t i = 0; i < vector_size; ++i) @@ -185,7 +206,7 @@ void PostProcessingConfigDiag::ConfigGrouping::GenerateUI(PostProcessingConfigDi // This may not be 100% spot on accurate since developers can have odd stepping intervals // set. // Round up so if it is outside our range, then set it to the minimum or maximum - steps = ceil(range / (double)m_config_option->m_integer_step_values[i]); + steps = std::ceil(range / (double)m_config_option->m_integer_step_values[i]); // Default value is just the currently set value here current_value = m_config_option->m_integer_values[i]; @@ -196,7 +217,7 @@ void PostProcessingConfigDiag::ConfigGrouping::GenerateUI(PostProcessingConfigDi // Same as above but with floats float range = m_config_option->m_float_max_values[i] - m_config_option->m_float_min_values[i]; - steps = ceil(range / m_config_option->m_float_step_values[i]); + steps = std::ceil(range / m_config_option->m_float_step_values[i]); // We need to convert our default float value from a float to the nearest step value range current_value = @@ -204,8 +225,9 @@ void PostProcessingConfigDiag::ConfigGrouping::GenerateUI(PostProcessingConfigDi string_value = std::to_string(m_config_option->m_float_values[i]); } - wxSlider* slider = new wxSlider(parent, wxID_ANY, current_value, 0, steps, wxDefaultPosition, - wxSize(200, -1), wxSL_HORIZONTAL | wxSL_BOTTOM); + DolphinSlider* slider = + new DolphinSlider(parent, wxID_ANY, current_value, 0, steps, wxDefaultPosition, + parent->FromDIP(wxSize(200, -1)), wxSL_HORIZONTAL | wxSL_BOTTOM); wxTextCtrl* text_ctrl = new wxTextCtrl(parent, wxID_ANY, string_value); // Disable the textctrl, it's only there to show the absolute value from the slider @@ -222,18 +244,18 @@ void PostProcessingConfigDiag::ConfigGrouping::GenerateUI(PostProcessingConfigDi if (vector_size == 1) { - szr_values->Add(m_option_sliders[0], wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL)); - szr_values->Add(m_option_text_ctrls[0]); + szr_values->Add(m_option_sliders[0], 0, wxALIGN_CENTER_VERTICAL); + szr_values->Add(m_option_text_ctrls[0], 0, wxALIGN_CENTER_VERTICAL); sizer->Add(szr_values); } else { - wxFlexGridSizer* const szr_inside = new wxFlexGridSizer(2, 0, 0); + wxFlexGridSizer* const szr_inside = new wxFlexGridSizer(2); for (size_t i = 0; i < vector_size; ++i) { - szr_inside->Add(m_option_sliders[i], wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL)); - szr_inside->Add(m_option_text_ctrls[i]); + szr_inside->Add(m_option_sliders[i], 0, wxALIGN_CENTER_VERTICAL); + szr_inside->Add(m_option_text_ctrls[i], 0, wxALIGN_CENTER_VERTICAL); } szr_values->Add(szr_inside); @@ -313,13 +335,3 @@ void PostProcessingConfigDiag::Event_Slider(wxCommandEvent& ev) } ev.Skip(); } - -void PostProcessingConfigDiag::Event_ClickClose(wxCommandEvent&) -{ - Close(); -} - -void PostProcessingConfigDiag::Event_Close(wxCloseEvent& ev) -{ - EndModal(wxID_OK); -} diff --git a/Source/Core/DolphinWX/PostProcessingConfigDiag.h b/Source/Core/DolphinWX/PostProcessingConfigDiag.h index fa896c1fb1..ec366e8635 100644 --- a/Source/Core/DolphinWX/PostProcessingConfigDiag.h +++ b/Source/Core/DolphinWX/PostProcessingConfigDiag.h @@ -4,14 +4,16 @@ #pragma once -#include <map> +#include <memory> #include <string> +#include <unordered_map> +#include <utility> #include <vector> #include <wx/dialog.h> -#include <wx/slider.h> #include <wx/textctrl.h> +#include "DolphinWX/DolphinSlider.h" #include "VideoCommon/PostProcessing.h" class wxButton; @@ -54,9 +56,12 @@ private: { } - void AddChild(ConfigGrouping* child) { m_children.push_back(child); } + void AddChild(std::unique_ptr<ConfigGrouping>&& child) + { + m_children.emplace_back(std::move(child)); + } bool HasChildren() { return m_children.size() != 0; } - std::vector<ConfigGrouping*>& GetChildren() { return m_children; } + const std::vector<std::unique_ptr<ConfigGrouping>>& GetChildren() { return m_children; } // Gets the string that is shown in the UI for the option const std::string& GetGUIName() { return m_gui_name; } // Gets the option name for use in the shader @@ -86,21 +91,19 @@ private: // For TYPE_SLIDER // Can have up to 4 - std::vector<wxSlider*> m_option_sliders; + std::vector<DolphinSlider*> m_option_sliders; std::vector<wxTextCtrl*> m_option_text_ctrls; - std::vector<ConfigGrouping*> m_children; + std::vector<std::unique_ptr<ConfigGrouping>> m_children; }; // WX UI things - void Event_Close(wxCloseEvent&); - void Event_ClickClose(wxCommandEvent&); void Event_Slider(wxCommandEvent& ev); void Event_CheckBox(wxCommandEvent& ev); const std::string& m_shader; PostProcessingShaderConfiguration* m_post_processor; - std::map<std::string, ConfigGrouping*> m_config_map; - std::vector<ConfigGrouping*> m_config_groups; + std::unordered_map<std::string, ConfigGrouping*> m_config_map; + std::vector<std::unique_ptr<ConfigGrouping>> m_config_groups; }; diff --git a/Source/Core/DolphinWX/SoftwareVideoConfigDialog.cpp b/Source/Core/DolphinWX/SoftwareVideoConfigDialog.cpp index 87c947bfcd..e8b29081a7 100644 --- a/Source/Core/DolphinWX/SoftwareVideoConfigDialog.cpp +++ b/Source/Core/DolphinWX/SoftwareVideoConfigDialog.cpp @@ -36,11 +36,12 @@ SoftwareVideoConfigDialog::SoftwareVideoConfigDialog(wxWindow* parent, const std wxString(wxString::Format(_("Dolphin %s Graphics Configuration"), title))) { VideoConfig& vconfig = g_Config; - vconfig.Load(File::GetUserPath(D_CONFIG_IDX) + "GFX.ini"); wxNotebook* const notebook = new wxNotebook(this, wxID_ANY); + const int space5 = FromDIP(5); + // -- GENERAL -- { wxPanel* const page_general = new wxPanel(notebook); @@ -51,9 +52,11 @@ SoftwareVideoConfigDialog::SoftwareVideoConfigDialog(wxWindow* parent, const std { wxStaticBoxSizer* const group_rendering = new wxStaticBoxSizer(wxVERTICAL, page_general, _("Rendering")); - szr_general->Add(group_rendering, 0, wxEXPAND | wxALL, 5); - wxGridSizer* const szr_rendering = new wxGridSizer(2, 5, 5); - group_rendering->Add(szr_rendering, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + szr_general->AddSpacer(space5); + szr_general->Add(group_rendering, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + wxGridSizer* const szr_rendering = new wxGridSizer(2, space5, space5); + group_rendering->Add(szr_rendering, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + group_rendering->AddSpacer(space5); // backend wxStaticText* const label_backend = new wxStaticText(page_general, wxID_ANY, _("Backend:")); @@ -68,8 +71,8 @@ SoftwareVideoConfigDialog::SoftwareVideoConfigDialog(wxWindow* parent, const std choice_backend->SetStringSelection(StrToWxStr(g_video_backend->GetName())); choice_backend->Bind(wxEVT_CHOICE, &SoftwareVideoConfigDialog::Event_Backend, this); - szr_rendering->Add(label_backend, 1, wxALIGN_CENTER_VERTICAL, 5); - szr_rendering->Add(choice_backend, 1, 0, 0); + szr_rendering->Add(label_backend, 0, wxALIGN_CENTER_VERTICAL); + szr_rendering->Add(choice_backend, 0, wxALIGN_CENTER_VERTICAL); if (Core::GetState() != Core::CORE_UNINITIALIZED) { @@ -86,9 +89,11 @@ SoftwareVideoConfigDialog::SoftwareVideoConfigDialog(wxWindow* parent, const std { wxStaticBoxSizer* const group_info = new wxStaticBoxSizer(wxVERTICAL, page_general, _("Overlay Information")); - szr_general->Add(group_info, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); - wxGridSizer* const szr_info = new wxGridSizer(2, 5, 5); - group_info->Add(szr_info, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + szr_general->AddSpacer(space5); + szr_general->Add(group_info, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + wxGridSizer* const szr_info = new wxGridSizer(2, space5, space5); + group_info->Add(szr_info, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + group_info->AddSpacer(space5); szr_info->Add( new SettingCheckBox(page_general, _("Various Statistics"), "", vconfig.bOverlayStats)); @@ -98,9 +103,11 @@ SoftwareVideoConfigDialog::SoftwareVideoConfigDialog(wxWindow* parent, const std { wxStaticBoxSizer* const group_utility = new wxStaticBoxSizer(wxVERTICAL, page_general, _("Utility")); - szr_general->Add(group_utility, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); - wxGridSizer* const szr_utility = new wxGridSizer(2, 5, 5); - group_utility->Add(szr_utility, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + szr_general->AddSpacer(space5); + szr_general->Add(group_utility, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + wxGridSizer* const szr_utility = new wxGridSizer(2, space5, space5); + group_utility->Add(szr_utility, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + group_utility->AddSpacer(space5); szr_utility->Add( new SettingCheckBox(page_general, _("Dump Textures"), "", vconfig.bDumpTextures)); @@ -110,10 +117,12 @@ SoftwareVideoConfigDialog::SoftwareVideoConfigDialog(wxWindow* parent, const std // - debug only wxStaticBoxSizer* const group_debug_only_utility = new wxStaticBoxSizer(wxHORIZONTAL, page_general, _("Debug Only")); - group_utility->Add(group_debug_only_utility, 0, wxEXPAND | wxBOTTOM, 5); - wxGridSizer* const szr_debug_only_utility = new wxGridSizer(2, 5, 5); - group_debug_only_utility->Add(szr_debug_only_utility, 1, - wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + group_utility->Add(group_debug_only_utility, 0, wxEXPAND); + group_utility->AddSpacer(space5); + wxGridSizer* const szr_debug_only_utility = new wxGridSizer(2, space5, space5); + group_debug_only_utility->AddSpacer(space5); + group_debug_only_utility->Add(szr_debug_only_utility, 0, wxEXPAND | wxBOTTOM, space5); + group_debug_only_utility->AddSpacer(space5); szr_debug_only_utility->Add( new SettingCheckBox(page_general, _("Dump TEV Stages"), "", vconfig.bDumpTevStages)); @@ -125,23 +134,33 @@ SoftwareVideoConfigDialog::SoftwareVideoConfigDialog(wxWindow* parent, const std { wxStaticBoxSizer* const group_misc = new wxStaticBoxSizer(wxVERTICAL, page_general, _("Drawn Object Range")); - szr_general->Add(group_misc, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); - wxFlexGridSizer* const szr_misc = new wxFlexGridSizer(2, 5, 5); - group_misc->Add(szr_misc, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + szr_general->AddSpacer(space5); + szr_general->Add(group_misc, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + wxFlexGridSizer* const szr_misc = new wxFlexGridSizer(2, space5, space5); + group_misc->Add(szr_misc, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + group_misc->AddSpacer(space5); szr_misc->Add( new IntegerSetting<int>(page_general, _("Start"), vconfig.drawStart, 0, 100000)); szr_misc->Add(new IntegerSetting<int>(page_general, _("End"), vconfig.drawEnd, 0, 100000)); } + szr_general->AddSpacer(space5); page_general->SetSizerAndFit(szr_general); } - wxBoxSizer* const szr_main = new wxBoxSizer(wxVERTICAL); - szr_main->Add(notebook, 1, wxEXPAND | wxALL, 5); - szr_main->Add(new wxButton(this, wxID_OK, _("Close"), wxDefaultPosition), 0, - wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 5); + wxStdDialogButtonSizer* const btn_sizer = CreateStdDialogButtonSizer(wxOK | wxNO_DEFAULT); + btn_sizer->GetAffirmativeButton()->SetLabel(_("Close")); + wxBoxSizer* const szr_main = new wxBoxSizer(wxVERTICAL); + szr_main->AddSpacer(space5); + szr_main->Add(notebook, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + szr_main->AddSpacer(space5); + szr_main->Add(btn_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + szr_main->AddSpacer(space5); + + SetLayoutAdaptationMode(wxDIALOG_ADAPTATION_MODE_ENABLED); + SetLayoutAdaptationLevel(wxDIALOG_ADAPTATION_STANDARD_SIZER); SetSizerAndFit(szr_main); Center(); SetFocus(); diff --git a/Source/Core/DolphinWX/TASInputDlg.cpp b/Source/Core/DolphinWX/TASInputDlg.cpp index 6bf57383b1..08b586bd97 100644 --- a/Source/Core/DolphinWX/TASInputDlg.cpp +++ b/Source/Core/DolphinWX/TASInputDlg.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include <algorithm> +#include <array> #include <cstddef> #include <wx/bitmap.h> #include <wx/checkbox.h> @@ -23,7 +25,9 @@ #include "Core/HW/WiimoteEmu/WiimoteEmu.h" #include "Core/HW/WiimoteReal/WiimoteReal.h" #include "Core/Movie.h" +#include "DolphinWX/DolphinSlider.h" #include "DolphinWX/TASInputDlg.h" +#include "DolphinWX/WxUtils.h" #include "InputCommon/GCPadStatus.h" #include "InputCommon/InputConfig.h" @@ -63,37 +67,38 @@ void TASInputDlg::CreateBaseLayout() m_controls[0] = &m_main_stick.x_cont; m_controls[1] = &m_main_stick.y_cont; - m_a = CreateButton("A"); + m_a = CreateButton(_("A")); m_a.checkbox->SetClientData(&m_a); - m_b = CreateButton("B"); + m_b = CreateButton(_("B")); m_b.checkbox->SetClientData(&m_b); - m_dpad_up = CreateButton("Up"); + m_dpad_up = CreateButton(_("Up")); m_dpad_up.checkbox->SetClientData(&m_dpad_up); - m_dpad_right = CreateButton("Right"); + m_dpad_right = CreateButton(_("Right")); m_dpad_right.checkbox->SetClientData(&m_dpad_right); - m_dpad_down = CreateButton("Down"); + m_dpad_down = CreateButton(_("Down")); m_dpad_down.checkbox->SetClientData(&m_dpad_down); - m_dpad_left = CreateButton("Left"); + m_dpad_left = CreateButton(_("Left")); m_dpad_left.checkbox->SetClientData(&m_dpad_left); m_buttons_dpad = new wxGridSizer(3); - m_buttons_dpad->AddSpacer(20); + const int space20 = FromDIP(20); + m_buttons_dpad->Add(space20, space20); m_buttons_dpad->Add(m_dpad_up.checkbox); - m_buttons_dpad->AddSpacer(20); + m_buttons_dpad->Add(space20, space20); m_buttons_dpad->Add(m_dpad_left.checkbox); - m_buttons_dpad->AddSpacer(20); + m_buttons_dpad->Add(space20, space20); m_buttons_dpad->Add(m_dpad_right.checkbox); - m_buttons_dpad->AddSpacer(20); + m_buttons_dpad->Add(space20, space20); m_buttons_dpad->Add(m_dpad_down.checkbox); - m_buttons_dpad->AddSpacer(20); + m_buttons_dpad->Add(space20, space20); } -const int TASInputDlg::m_gc_pad_buttons_bitmask[12] = { +static constexpr int s_gc_pad_buttons_bitmask[12] = { PAD_BUTTON_DOWN, PAD_BUTTON_UP, PAD_BUTTON_LEFT, PAD_BUTTON_RIGHT, PAD_BUTTON_A, PAD_BUTTON_B, PAD_BUTTON_X, PAD_BUTTON_Y, PAD_TRIGGER_Z, PAD_TRIGGER_L, PAD_TRIGGER_R, PAD_BUTTON_START}; -const int TASInputDlg::m_wii_buttons_bitmask[11] = { +static constexpr int s_wii_buttons_bitmask[11] = { WiimoteEmu::Wiimote::PAD_DOWN, WiimoteEmu::Wiimote::PAD_UP, WiimoteEmu::Wiimote::PAD_LEFT, WiimoteEmu::Wiimote::PAD_RIGHT, WiimoteEmu::Wiimote::BUTTON_A, WiimoteEmu::Wiimote::BUTTON_B, @@ -102,7 +107,7 @@ const int TASInputDlg::m_wii_buttons_bitmask[11] = { WiimoteEmu::Wiimote::BUTTON_HOME, }; -const int TASInputDlg::m_cc_buttons_bitmask[15] = { +static constexpr int s_cc_buttons_bitmask[15] = { WiimoteEmu::Classic::PAD_DOWN, WiimoteEmu::Classic::PAD_UP, WiimoteEmu::Classic::PAD_LEFT, WiimoteEmu::Classic::PAD_RIGHT, WiimoteEmu::Classic::BUTTON_A, WiimoteEmu::Classic::BUTTON_B, @@ -113,13 +118,11 @@ const int TASInputDlg::m_cc_buttons_bitmask[15] = { WiimoteEmu::Classic::BUTTON_HOME, }; -const std::string TASInputDlg::m_cc_button_names[] = { - "Down", "Up", "Left", "Right", "A", "B", "X", "Y", "+", "-", "L", "R", "ZR", "ZL", "Home"}; - void TASInputDlg::CreateWiiLayout(int num) { if (m_has_layout) return; + const int space5 = FromDIP(5); CreateBaseLayout(); @@ -142,23 +145,17 @@ void TASInputDlg::CreateWiiLayout(int num) wxStaticBoxSizer* const axisBox = CreateAccelLayout(&m_x_cont, &m_y_cont, &m_z_cont, _("Orientation")); - wxStaticBoxSizer* const m_buttons_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Buttons")); - wxGridSizer* const m_buttons_grid = new wxGridSizer(4); - - m_plus = CreateButton("+"); + m_plus = CreateButton(_("+")); m_plus.checkbox->SetClientData(&m_plus); - m_minus = CreateButton("-"); + m_minus = CreateButton(_("-")); m_minus.checkbox->SetClientData(&m_minus); - m_one = CreateButton("1"); + m_one = CreateButton(_("1")); m_one.checkbox->SetClientData(&m_one); - m_two = CreateButton("2"); + m_two = CreateButton(_("2")); m_two.checkbox->SetClientData(&m_two); - m_home = CreateButton("Home"); + m_home = CreateButton(_("Home")); m_home.checkbox->SetClientData(&m_home); - m_main_szr = new wxBoxSizer(wxVERTICAL); - m_wiimote_szr = new wxBoxSizer(wxHORIZONTAL); - m_ext_szr = new wxBoxSizer(wxHORIZONTAL); m_cc_szr = CreateCCLayout(); if (Core::IsRunning()) @@ -196,12 +193,10 @@ void TASInputDlg::CreateWiiLayout(int num) wxStaticBoxSizer* const nunchukaxisBox = CreateAccelLayout(&m_nx_cont, &m_ny_cont, &m_nz_cont, _("Nunchuk orientation")); - m_c = CreateButton("C"); + m_c = CreateButton(_("C")); m_c.checkbox->SetClientData(&m_c); - m_z = CreateButton("Z"); + m_z = CreateButton(_("Z")); m_z.checkbox->SetClientData(&m_z); - m_ext_szr->Add(m_c_stick_szr, 0, wxLEFT | wxBOTTOM | wxRIGHT, 5); - m_ext_szr->Add(nunchukaxisBox); for (Control* const control : m_controls) { @@ -209,21 +204,35 @@ void TASInputDlg::CreateWiiLayout(int num) control->slider->Bind(wxEVT_RIGHT_UP, &TASInputDlg::OnRightClickSlider, this); } + m_ext_szr = new wxBoxSizer(wxHORIZONTAL); + m_ext_szr->Add(m_c_stick_szr, 0, wxBOTTOM, space5); + m_ext_szr->AddSpacer(space5); + m_ext_szr->Add(nunchukaxisBox, 0, wxBOTTOM, space5); + + wxGridSizer* const buttons_grid = new wxGridSizer(4); for (unsigned int i = 4; i < ArraySize(m_buttons); ++i) if (m_buttons[i] != nullptr) - m_buttons_grid->Add(m_buttons[i]->checkbox); - m_buttons_grid->AddSpacer(5); + buttons_grid->Add(m_buttons[i]->checkbox); + buttons_grid->Add(space5, space5); - m_buttons_box->Add(m_buttons_grid); - m_buttons_box->Add(m_buttons_dpad); + wxStaticBoxSizer* const buttons_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Buttons")); + buttons_box->Add(buttons_grid); + buttons_box->Add(m_buttons_dpad, 0, wxTOP, space5); - m_wiimote_szr->Add(m_main_stick_szr, 0, wxALL, 5); - m_wiimote_szr->Add(axisBox, 0, wxTOP | wxRIGHT, 5); - m_wiimote_szr->Add(m_buttons_box, 0, wxTOP | wxRIGHT, 5); - m_main_szr->Add(m_wiimote_szr); - m_main_szr->Add(m_ext_szr); - m_main_szr->Add(m_cc_szr); + m_wiimote_szr = new wxBoxSizer(wxHORIZONTAL); + m_wiimote_szr->AddSpacer(space5); + m_wiimote_szr->Add(m_main_stick_szr); + m_wiimote_szr->Add(axisBox, 0, wxLEFT, space5); + m_wiimote_szr->Add(buttons_box, 0, wxLEFT, space5); + m_wiimote_szr->AddSpacer(space5); + // NOTE: Not all of these are visible at the same time. + m_main_szr = new wxBoxSizer(wxVERTICAL); + m_main_szr->Add(m_wiimote_szr, 0, wxTOP | wxBOTTOM, space5); + m_main_szr->Add(m_ext_szr, 0, wxLEFT | wxRIGHT, space5); + m_main_szr->Add(m_cc_szr, 0, wxLEFT | wxRIGHT, space5); + + SetSizer(m_main_szr); HandleExtensionChange(); FinishLayout(); } @@ -239,11 +248,12 @@ void TASInputDlg::FinishLayout() wxBoxSizer* TASInputDlg::CreateCCLayout() { - wxBoxSizer* const szr = new wxBoxSizer(wxHORIZONTAL); - - for (size_t i = 0; i < ArraySize(m_cc_buttons); ++i) + const std::array<wxString, 15> button_names{{_("Down"), _("Up"), _("Left"), _("Right"), _("A"), + _("B"), _("X"), _("Y"), _("+"), _("-"), _("L"), + _("R"), _("ZR"), _("ZL"), _("Home")}}; + for (size_t i = 0; i < button_names.size(); ++i) { - m_cc_buttons[i] = CreateButton(m_cc_button_names[i]); + m_cc_buttons[i] = CreateButton(button_names[i]); m_cc_buttons[i].checkbox->SetClientData(&m_cc_buttons[i]); } @@ -265,6 +275,9 @@ wxBoxSizer* TASInputDlg::CreateCCLayout() m_cc_l = CreateControl(wxSL_VERTICAL, -1, 100, false, 31, 0); m_cc_r = CreateControl(wxSL_VERTICAL, -1, 100, false, 31, 0); + const int space5 = FromDIP(5); + const int space20 = FromDIP(20); + wxStaticBoxSizer* const shoulder_box = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Shoulder Buttons")); shoulder_box->Add(m_cc_l.slider, 0, wxALIGN_CENTER_VERTICAL); @@ -272,32 +285,37 @@ wxBoxSizer* TASInputDlg::CreateCCLayout() shoulder_box->Add(m_cc_r.slider, 0, wxALIGN_CENTER_VERTICAL); shoulder_box->Add(m_cc_r.text, 0, wxALIGN_CENTER_VERTICAL); - wxStaticBoxSizer* const cc_buttons_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Buttons")); - wxGridSizer* const cc_buttons_grid = new wxGridSizer(4); wxGridSizer* const cc_buttons_dpad = new wxGridSizer(3); - - cc_buttons_dpad->AddSpacer(20); + cc_buttons_dpad->Add(space20, space20); cc_buttons_dpad->Add(m_cc_buttons[1].checkbox); - cc_buttons_dpad->AddSpacer(20); + cc_buttons_dpad->Add(space20, space20); cc_buttons_dpad->Add(m_cc_buttons[2].checkbox); - cc_buttons_dpad->AddSpacer(20); + cc_buttons_dpad->Add(space20, space20); cc_buttons_dpad->Add(m_cc_buttons[3].checkbox); - cc_buttons_dpad->AddSpacer(20); + cc_buttons_dpad->Add(space20, space20); cc_buttons_dpad->Add(m_cc_buttons[0].checkbox); - cc_buttons_dpad->AddSpacer(20); + cc_buttons_dpad->Add(space20, space20); - for (auto button : m_cc_buttons) + wxGridSizer* const cc_buttons_grid = new wxGridSizer(4); + for (auto& button : m_cc_buttons) if (!button.checkbox->GetContainingSizer()) cc_buttons_grid->Add(button.checkbox); - cc_buttons_grid->AddSpacer(5); + cc_buttons_grid->Add(space5, space5); + wxStaticBoxSizer* const cc_buttons_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Buttons")); cc_buttons_box->Add(cc_buttons_grid); - cc_buttons_box->Add(cc_buttons_dpad); + cc_buttons_box->Add(cc_buttons_dpad, 0, wxTOP, space5); - szr->Add(m_cc_l_stick_szr, 0, wxALL, 5); - szr->Add(m_cc_r_stick_szr, 0, wxALL, 5); - szr->Add(shoulder_box, 0, wxLEFT | wxRIGHT, 5); - szr->Add(cc_buttons_box, 0, wxTOP | wxRIGHT, 5); + wxBoxSizer* const szr = new wxBoxSizer(wxHORIZONTAL); + szr->AddSpacer(space5); + szr->Add(m_cc_l_stick_szr, 0, wxTOP | wxBOTTOM, space5); + szr->AddSpacer(space5); + szr->Add(m_cc_r_stick_szr, 0, wxTOP | wxBOTTOM, space5); + szr->AddSpacer(space5); + szr->Add(shoulder_box, 0, wxTOP | wxBOTTOM, space5); + szr->AddSpacer(space5); + szr->Add(cc_buttons_box, 0, wxTOP | wxBOTTOM, space5); + szr->AddSpacer(space5); for (Control* const control : m_cc_controls) { @@ -327,8 +345,9 @@ void TASInputDlg::HandleExtensionChange() m_main_szr->Hide(m_cc_szr); m_main_szr->Show(m_wiimote_szr); } - SetSizerAndFit(m_main_szr, true); ResetValues(); + m_main_szr->SetSizeHints(this); + Layout(); } void TASInputDlg::CreateGCLayout() @@ -350,8 +369,6 @@ void TASInputDlg::CreateGCLayout() m_controls[4] = &m_l_cont; m_controls[5] = &m_r_cont; - wxBoxSizer* const top_box = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* const bottom_box = new wxBoxSizer(wxHORIZONTAL); m_main_stick = CreateStick(ID_MAIN_STICK, 255, 255, 128, 128, false, true); wxStaticBoxSizer* const main_box = CreateStickLayout(&m_main_stick, _("Main Stick")); @@ -373,38 +390,45 @@ void TASInputDlg::CreateGCLayout() control->slider->Bind(wxEVT_RIGHT_UP, &TASInputDlg::OnRightClickSlider, this); } - wxStaticBoxSizer* const m_buttons_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Buttons")); - wxGridSizer* const m_buttons_grid = new wxGridSizer(4); - - m_x = CreateButton("X"); + m_x = CreateButton(_("X")); m_x.checkbox->SetClientData(&m_x); - m_y = CreateButton("Y"); + m_y = CreateButton(_("Y")); m_y.checkbox->SetClientData(&m_y); - m_l = CreateButton("L"); + m_l = CreateButton(_("L")); m_l.checkbox->SetClientData(&m_l); - m_r = CreateButton("R"); + m_r = CreateButton(_("R")); m_r.checkbox->SetClientData(&m_r); - m_z = CreateButton("Z"); + m_z = CreateButton(_("Z")); m_z.checkbox->SetClientData(&m_z); - m_start = CreateButton("Start"); + m_start = CreateButton(_("Start")); m_start.checkbox->SetClientData(&m_start); + const int space5 = FromDIP(5); + + wxGridSizer* const buttons_grid = new wxGridSizer(4); for (unsigned int i = 4; i < ArraySize(m_buttons); ++i) if (m_buttons[i] != nullptr) - m_buttons_grid->Add(m_buttons[i]->checkbox, false); - m_buttons_grid->AddSpacer(5); + buttons_grid->Add(m_buttons[i]->checkbox, false); + buttons_grid->Add(space5, space5); - m_buttons_box->Add(m_buttons_grid); - m_buttons_box->Add(m_buttons_dpad); + wxStaticBoxSizer* const buttons_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Buttons")); + buttons_box->Add(buttons_grid); + buttons_box->Add(m_buttons_dpad); + + wxBoxSizer* const top_box = new wxBoxSizer(wxHORIZONTAL); + top_box->Add(main_box); + top_box->Add(c_box, 0, wxLEFT, space5); + + wxBoxSizer* const bottom_box = new wxBoxSizer(wxHORIZONTAL); + bottom_box->Add(shoulder_box); + bottom_box->Add(buttons_box, 0, wxLEFT, space5); wxBoxSizer* const main_szr = new wxBoxSizer(wxVERTICAL); - - top_box->Add(main_box, 0, wxALL, 5); - top_box->Add(c_box, 0, wxTOP | wxRIGHT, 5); - bottom_box->Add(shoulder_box, 0, wxLEFT | wxRIGHT, 5); - bottom_box->Add(m_buttons_box, 0, wxBOTTOM, 5); - main_szr->Add(top_box); - main_szr->Add(bottom_box); + main_szr->AddSpacer(space5); + main_szr->Add(top_box, 0, wxLEFT | wxRIGHT, space5); + main_szr->AddSpacer(space5); + main_szr->Add(bottom_box, 0, wxLEFT | wxRIGHT, space5); + main_szr->AddSpacer(space5); SetSizerAndFit(main_szr); ResetValues(); @@ -414,21 +438,20 @@ void TASInputDlg::CreateGCLayout() TASInputDlg::Control TASInputDlg::CreateControl(long style, int width, int height, bool reverse, u32 range, u32 default_value) { - Control tempCont; - tempCont.range = range; - tempCont.default_value = default_value; - tempCont.slider = new wxSlider(this, m_eleID++, default_value, 0, range, wxDefaultPosition, - wxDefaultSize, style); - tempCont.slider->SetMinSize(wxSize(width, height)); - tempCont.slider->Bind(wxEVT_SLIDER, &TASInputDlg::UpdateFromSliders, this); - tempCont.text = new wxTextCtrl(this, m_eleID++, std::to_string(default_value), wxDefaultPosition, - wxSize(40, 20)); - tempCont.text->SetMaxLength(range > 999 ? 4 : 3); - tempCont.text_id = m_eleID - 1; - tempCont.text->Bind(wxEVT_TEXT, &TASInputDlg::UpdateFromText, this); - tempCont.slider_id = m_eleID - 2; - tempCont.reverse = reverse; - return tempCont; + Control control; + control.range = range; + control.default_value = default_value; + control.slider_id = m_eleID++; + control.slider = new DolphinSlider(this, control.slider_id, default_value, 0, range, + wxDefaultPosition, FromDIP(wxSize(width, height)), style); + control.slider->Bind(wxEVT_SLIDER, &TASInputDlg::UpdateFromSliders, this); + control.text_id = m_eleID++; + control.text = new wxTextCtrl(this, control.text_id, std::to_string(default_value)); + control.text->SetMaxLength(range > 999 ? 4 : 3); + control.text->SetMinSize(WxUtils::GetTextWidgetMinSize(control.text, range)); + control.text->Bind(wxEVT_TEXT, &TASInputDlg::UpdateFromText, this); + control.reverse = reverse; + return control; } TASInputDlg::Stick TASInputDlg::CreateStick(int id_stick, int xRange, int yRange, u32 defaultX, @@ -446,18 +469,23 @@ TASInputDlg::Stick TASInputDlg::CreateStick(int id_stick, int xRange, int yRange return tempStick; } -wxStaticBoxSizer* TASInputDlg::CreateStickLayout(Stick* tempStick, const wxString& title) +wxStaticBoxSizer* TASInputDlg::CreateStickLayout(Stick* stick, const wxString& title) { - wxStaticBoxSizer* const temp_box = new wxStaticBoxSizer(wxHORIZONTAL, this, title); - wxFlexGridSizer* grid = new wxFlexGridSizer(2, 3, 3); + const int space3 = FromDIP(3); - grid->Add(tempStick->x_cont.slider, 0, wxALIGN_CENTER_HORIZONTAL | wxALIGN_BOTTOM); - grid->Add(tempStick->x_cont.text, 0, wxEXPAND); - grid->Add(tempStick->bitmap, 0, wxALL | wxALIGN_CENTER, 3); - grid->Add(tempStick->y_cont.slider, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT); - grid->Add(1, 1); - grid->Add(tempStick->y_cont.text, 0, wxEXPAND); - temp_box->Add(grid, 1, wxEXPAND | wxALL, 3); + wxStaticBoxSizer* const temp_box = new wxStaticBoxSizer(wxVERTICAL, this, title); + wxFlexGridSizer* const layout = new wxFlexGridSizer(2, space3, space3); + + layout->Add(stick->x_cont.slider, 0, wxEXPAND); + layout->Add(stick->x_cont.text, 0, wxALIGN_CENTER); + layout->Add(stick->bitmap, 0, wxALIGN_RIGHT); + layout->Add(stick->y_cont.slider, 0, wxEXPAND); + layout->AddSpacer(1); // Placeholder for unused cell + layout->Add(stick->y_cont.text, 0, wxALIGN_CENTER); + + temp_box->AddSpacer(space3); + temp_box->Add(layout, 0, wxLEFT | wxRIGHT, space3); + temp_box->AddSpacer(space3); return temp_box; } @@ -468,6 +496,7 @@ wxStaticBoxSizer* TASInputDlg::CreateAccelLayout(Control* x, Control* y, Control wxStaticBoxSizer* const xBox = new wxStaticBoxSizer(wxVERTICAL, this, _("X")); wxStaticBoxSizer* const yBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Y")); wxStaticBoxSizer* const zBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Z")); + const int space5 = FromDIP(5); xBox->Add(x->slider, 0, wxALIGN_CENTER_HORIZONTAL); xBox->Add(x->text, 0, wxALIGN_CENTER_HORIZONTAL); @@ -475,21 +504,25 @@ wxStaticBoxSizer* TASInputDlg::CreateAccelLayout(Control* x, Control* y, Control yBox->Add(y->text, 0, wxALIGN_CENTER_HORIZONTAL); zBox->Add(z->slider, 0, wxALIGN_CENTER_HORIZONTAL); zBox->Add(z->text, 0, wxALIGN_CENTER_HORIZONTAL); - temp_box->Add(xBox, 0, wxLEFT | wxBOTTOM | wxRIGHT, 5); - temp_box->Add(yBox, 0, wxRIGHT, 5); - temp_box->Add(zBox, 0, wxRIGHT, 5); + temp_box->AddSpacer(space5); + temp_box->Add(xBox, 0, wxBOTTOM, space5); + temp_box->AddSpacer(space5); + temp_box->Add(yBox, 0, wxBOTTOM, space5); + temp_box->AddSpacer(space5); + temp_box->Add(zBox, 0, wxBOTTOM, space5); + temp_box->AddSpacer(space5); return temp_box; } -TASInputDlg::Button TASInputDlg::CreateButton(const std::string& name) +TASInputDlg::Button TASInputDlg::CreateButton(const wxString& name) { Button temp; - wxCheckBox* checkbox = new wxCheckBox(this, m_eleID++, name); + temp.id = m_eleID++; + wxCheckBox* checkbox = new wxCheckBox(this, temp.id, name); checkbox->Bind(wxEVT_RIGHT_DOWN, &TASInputDlg::SetTurbo, this); checkbox->Bind(wxEVT_LEFT_DOWN, &TASInputDlg::SetTurbo, this); checkbox->Bind(wxEVT_CHECKBOX, &TASInputDlg::OnCheckboxToggle, this); temp.checkbox = checkbox; - temp.id = m_eleID - 1; return temp; } @@ -606,7 +639,7 @@ void TASInputDlg::SetWiiButtons(u16* butt) for (unsigned int i = 0; i < 11; ++i) { if (m_buttons[i] != nullptr) - *butt |= (m_buttons[i]->is_checked) ? m_wii_buttons_bitmask[i] : 0; + *butt |= (m_buttons[i]->is_checked) ? s_wii_buttons_bitmask[i] : 0; } ButtonTurbo(); } @@ -625,7 +658,7 @@ void TASInputDlg::GetKeyBoardInput(GCPadStatus* PadStatus) for (unsigned int i = 0; i < ArraySize(m_buttons); ++i) { if (m_buttons[i] != nullptr) - SetButtonValue(m_buttons[i], ((PadStatus->button & m_gc_pad_buttons_bitmask[i]) != 0)); + SetButtonValue(m_buttons[i], ((PadStatus->button & s_gc_pad_buttons_bitmask[i]) != 0)); } SetButtonValue(&m_l, ((PadStatus->triggerLeft) == 255) || ((PadStatus->button & PAD_TRIGGER_L) != 0)); @@ -648,7 +681,7 @@ void TASInputDlg::GetKeyBoardInput(u8* data, WiimoteEmu::ReportFeatures rptf, in { if (m_buttons[i] != nullptr) SetButtonValue(m_buttons[i], - (((wm_buttons*)coreData)->hex & m_wii_buttons_bitmask[i]) != 0); + (((wm_buttons*)coreData)->hex & s_wii_buttons_bitmask[i]) != 0); } } if (accelData) @@ -688,7 +721,7 @@ void TASInputDlg::GetKeyBoardInput(u8* data, WiimoteEmu::ReportFeatures rptf, in cc.bt.hex = cc.bt.hex ^ 0xFFFF; for (unsigned int i = 0; i < 15; ++i) { - SetButtonValue(&m_cc_buttons[i], ((cc.bt.hex & m_cc_buttons_bitmask[i]) != 0)); + SetButtonValue(&m_cc_buttons[i], ((cc.bt.hex & s_cc_buttons_bitmask[i]) != 0)); } if (m_cc_l.value == 31) @@ -835,7 +868,7 @@ void TASInputDlg::GetValues(u8* data, WiimoteEmu::ReportFeatures rptf, int ext, for (unsigned int i = 0; i < ArraySize(m_cc_buttons); ++i) { - cc.bt.hex |= (m_cc_buttons[i].is_checked) ? m_cc_buttons_bitmask[i] : 0; + cc.bt.hex |= (m_cc_buttons[i].is_checked) ? s_cc_buttons_bitmask[i] : 0; } cc.bt.hex ^= 0xFFFF; @@ -877,9 +910,9 @@ void TASInputDlg::GetValues(GCPadStatus* PadStatus) if (m_buttons[i] != nullptr) { if (m_buttons[i]->is_checked) - PadStatus->button |= m_gc_pad_buttons_bitmask[i]; + PadStatus->button |= s_gc_pad_buttons_bitmask[i]; else - PadStatus->button &= ~m_gc_pad_buttons_bitmask[i]; + PadStatus->button &= ~s_gc_pad_buttons_bitmask[i]; } } @@ -1042,8 +1075,9 @@ void TASInputDlg::OnMouseDownL(wxMouseEvent& event) return; wxPoint ptM(event.GetPosition()); - stick->x_cont.value = ptM.x * stick->x_cont.range / 127; - stick->y_cont.value = ptM.y * stick->y_cont.range / 127; + wxSize bitmap_size = FromDIP(wxSize(127, 127)); + stick->x_cont.value = ptM.x * stick->x_cont.range / bitmap_size.GetWidth(); + stick->y_cont.value = ptM.y * stick->y_cont.range / bitmap_size.GetHeight(); if ((unsigned)stick->y_cont.value > stick->y_cont.range) stick->y_cont.value = stick->y_cont.range; @@ -1055,12 +1089,8 @@ void TASInputDlg::OnMouseDownL(wxMouseEvent& event) if (stick->x_cont.reverse) stick->x_cont.value = stick->x_cont.range - (u16)stick->x_cont.value; - stick->x_cont.value = (unsigned int)stick->x_cont.value > stick->x_cont.range ? - stick->x_cont.range : - stick->x_cont.value; - stick->y_cont.value = (unsigned int)stick->y_cont.value > stick->y_cont.range ? - stick->y_cont.range : - stick->y_cont.value; + stick->x_cont.value = std::min<u32>(stick->x_cont.value, stick->x_cont.range); + stick->y_cont.value = std::min<u32>(stick->y_cont.value, stick->y_cont.range); // This updates sliders and the bitmap too. stick->x_cont.text->SetValue(std::to_string(stick->x_cont.value)); @@ -1188,9 +1218,19 @@ wxBitmap TASInputDlg::CreateStickBitmap(int x, int y) x = x / 2; y = y / 2; + // Scale for screen DPI + static constexpr int WIDTH = 129; + static constexpr int HEIGHT = 129; + wxSize bitmap_size = FromDIP(wxSize(WIDTH, HEIGHT)); + double scale_x = bitmap_size.GetWidth() / static_cast<double>(WIDTH); + double scale_y = bitmap_size.GetHeight() / static_cast<double>(HEIGHT); + wxMemoryDC memDC; - wxBitmap bitmap(129, 129); + wxBitmap bitmap; + bitmap.CreateScaled(bitmap_size.GetWidth(), bitmap_size.GetHeight(), wxBITMAP_SCREEN_DEPTH, + GetContentScaleFactor()); memDC.SelectObject(bitmap); + memDC.SetUserScale(scale_x, scale_y); memDC.SetBackground(*wxLIGHT_GREY_BRUSH); memDC.Clear(); memDC.SetBrush(*wxWHITE_BRUSH); @@ -1198,7 +1238,8 @@ wxBitmap TASInputDlg::CreateStickBitmap(int x, int y) memDC.SetPen(wxPen(*wxBLACK, 3, wxPENSTYLE_SOLID)); memDC.DrawLine(64, 64, x, y); memDC.SetPen(*wxBLACK_PEN); - memDC.CrossHair(64, 64); + memDC.DrawLine(64, 0, 64, HEIGHT); // CrossHair doesn't work @96DPI on Windows for some reason + memDC.DrawLine(0, 64, WIDTH, 64); memDC.SetBrush(*wxBLUE_BRUSH); memDC.DrawCircle(x, y, 5); memDC.SelectObject(wxNullBitmap); diff --git a/Source/Core/DolphinWX/TASInputDlg.h b/Source/Core/DolphinWX/TASInputDlg.h index 0e44f292e1..5fee27b792 100644 --- a/Source/Core/DolphinWX/TASInputDlg.h +++ b/Source/Core/DolphinWX/TASInputDlg.h @@ -13,8 +13,8 @@ #include "Core/HW/WiimoteEmu/WiimoteEmu.h" #include "InputCommon/GCPadStatus.h" +class DolphinSlider; class wxCheckBox; -class wxSlider; class wxStaticBitmap; class wxTextCtrl; @@ -25,36 +25,24 @@ public: const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE | wxSTAY_ON_TOP); - void OnCloseWindow(wxCloseEvent& event); - void UpdateFromSliders(wxCommandEvent& event); - void UpdateFromText(wxCommandEvent& event); - void OnMouseDownL(wxMouseEvent& event); - void OnMouseUpR(wxMouseEvent& event); - void OnRightClickSlider(wxMouseEvent& event); - void ResetValues(); void GetValues(GCPadStatus* PadStatus); void GetValues(u8* data, WiimoteEmu::ReportFeatures rptf, int ext, const wiimote_key key); - void SetTurbo(wxMouseEvent& event); - void ButtonTurbo(); void GetKeyBoardInput(GCPadStatus* PadStatus); void GetKeyBoardInput(u8* data, WiimoteEmu::ReportFeatures rptf, int ext, const wiimote_key key); void CreateGCLayout(); void CreateWiiLayout(int num); - wxBitmap CreateStickBitmap(int x, int y); - void SetWiiButtons(u16* butt); - void HandleExtensionChange(); private: - const int ID_C_STICK = 1001; - const int ID_MAIN_STICK = 1002; - const int ID_CC_L_STICK = 1003; - const int ID_CC_R_STICK = 1004; + static constexpr int ID_C_STICK = 1001; + static constexpr int ID_MAIN_STICK = 1002; + static constexpr int ID_CC_L_STICK = 1003; + static constexpr int ID_CC_R_STICK = 1004; int m_eleID = 1005; struct Control { wxTextCtrl* text; - wxSlider* slider; + DolphinSlider* slider; int value = -1; int text_id; int slider_id; @@ -100,9 +88,22 @@ private: bool reverseY); wxStaticBoxSizer* CreateStickLayout(Stick* tempStick, const wxString& title); wxStaticBoxSizer* CreateAccelLayout(Control* x, Control* y, Control* z, const wxString& title); - Button CreateButton(const std::string& name); + Button CreateButton(const wxString& name); Control CreateControl(long style, int width, int height, bool reverse = false, u32 range = 255, u32 default_value = 128); + wxBitmap CreateStickBitmap(int x, int y); + + void OnCloseWindow(wxCloseEvent& event); + void UpdateFromSliders(wxCommandEvent& event); + void UpdateFromText(wxCommandEvent& event); + void OnMouseDownL(wxMouseEvent& event); + void OnMouseUpR(wxMouseEvent& event); + void OnRightClickSlider(wxMouseEvent& event); + void SetTurbo(wxMouseEvent& event); + void ButtonTurbo(); + void HandleExtensionChange(); + void ResetValues(); + void SetWiiButtons(u16* butt); enum { @@ -127,10 +128,6 @@ private: Button m_cc_buttons[15]; Control* m_controls[10]; Control* m_cc_controls[6]; - static const int m_gc_pad_buttons_bitmask[12]; - static const int m_wii_buttons_bitmask[11]; - static const int m_cc_buttons_bitmask[15]; - static const std::string m_cc_button_names[15]; u8 m_ext = 0; wxBoxSizer* m_main_szr; wxBoxSizer* m_wiimote_szr; diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp index 147ff94376..38fe322b01 100644 --- a/Source/Core/DolphinWX/VideoConfigDiag.cpp +++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <algorithm> +#include <array> #include <map> #include <string> #include <utility> @@ -12,11 +13,11 @@ #include <wx/choice.h> #include <wx/control.h> #include <wx/dialog.h> +#include <wx/gbsizer.h> #include <wx/notebook.h> #include <wx/panel.h> #include <wx/radiobut.h> #include <wx/sizer.h> -#include <wx/slider.h> #include <wx/stattext.h> #include "Common/Assert.h" @@ -76,18 +77,14 @@ void SettingChoice::UpdateValue(wxCommandEvent& ev) ev.Skip(); } -void VideoConfigDiag::Event_ClickClose(wxCommandEvent&) -{ - Close(); -} - -void VideoConfigDiag::Event_Close(wxCloseEvent& ev) +void VideoConfigDiag::Event_Close(wxCommandEvent& ev) { g_Config.Save(File::GetUserPath(D_CONFIG_IDX) + "GFX.ini"); - - EndModal(wxID_OK); + ev.Skip(); } +static wxString default_desc = + wxTRANSLATE("Move the mouse pointer over an option to display a detailed description."); #if defined(_WIN32) static wxString backend_desc = wxTRANSLATE("Selects what graphics API to use internally.\nThe software renderer is extremely " @@ -352,6 +349,7 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) Bind(wxEVT_UPDATE_UI, &VideoConfigDiag::OnUpdateUI, this); wxNotebook* const notebook = new wxNotebook(this, wxID_ANY); + const int space5 = FromDIP(5); // -- GENERAL -- { @@ -361,7 +359,7 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) // - basic { - wxFlexGridSizer* const szr_basic = new wxFlexGridSizer(2, 5, 5); + wxFlexGridSizer* const szr_basic = new wxFlexGridSizer(2, space5, space5); // backend { @@ -378,8 +376,8 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) wxGetTranslation(StrToWxStr(g_video_backend->GetDisplayName()))); choice_backend->Bind(wxEVT_CHOICE, &VideoConfigDiag::Event_Backend, this); - szr_basic->Add(label_backend, 1, wxALIGN_CENTER_VERTICAL, 5); - szr_basic->Add(choice_backend, 1, 0, 0); + szr_basic->Add(label_backend, 0, wxALIGN_CENTER_VERTICAL); + szr_basic->Add(choice_backend, 0, wxALIGN_CENTER_VERTICAL); } // adapter (D3D only) @@ -396,12 +394,12 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) choice_adapter->Select(vconfig.iAdapter); label_adapter = new wxStaticText(page_general, wxID_ANY, _("Adapter:")); - szr_basic->Add(label_adapter, 1, wxALIGN_CENTER_VERTICAL, 5); - szr_basic->Add(choice_adapter, 1, 0, 0); + szr_basic->Add(label_adapter, 0, wxALIGN_CENTER_VERTICAL); + szr_basic->Add(choice_adapter, 0, wxALIGN_CENTER_VERTICAL); } // - display - wxFlexGridSizer* const szr_display = new wxFlexGridSizer(2, 5, 5); + wxFlexGridSizer* const szr_display = new wxFlexGridSizer(2, space5, space5); { #if !defined(__APPLE__) @@ -420,9 +418,12 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) choice_display_resolution->SetStringSelection( StrToWxStr(SConfig::GetInstance().strFullscreenResolution)); + // "Auto" is used as a keyword, convert to translated string + if (SConfig::GetInstance().strFullscreenResolution == "Auto") + choice_display_resolution->SetSelection(0); - szr_display->Add(label_display_resolution, 1, wxALIGN_CENTER_VERTICAL, 0); - szr_display->Add(choice_display_resolution); + szr_display->Add(label_display_resolution, 0, wxALIGN_CENTER_VERTICAL); + szr_display->Add(choice_display_resolution, 0, wxALIGN_CENTER_VERTICAL); } #endif @@ -431,12 +432,12 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) const wxString ar_choices[] = {_("Auto"), _("Force 16:9"), _("Force 4:3"), _("Stretch to Window")}; - szr_display->Add(new wxStaticText(page_general, wxID_ANY, _("Aspect Ratio:")), 1, - wxALIGN_CENTER_VERTICAL, 0); + szr_display->Add(new wxStaticText(page_general, wxID_ANY, _("Aspect Ratio:")), 0, + wxALIGN_CENTER_VERTICAL); wxChoice* const choice_aspect = CreateChoice(page_general, vconfig.iAspectRatio, wxGetTranslation(ar_desc), sizeof(ar_choices) / sizeof(*ar_choices), ar_choices); - szr_display->Add(choice_aspect, 1, 0, 0); + szr_display->Add(choice_aspect, 0, wxALIGN_CENTER_VERTICAL); } // various other display options @@ -450,7 +451,7 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) } // - other - wxFlexGridSizer* const szr_other = new wxFlexGridSizer(2, 5, 5); + wxFlexGridSizer* const szr_other = new wxFlexGridSizer(2, space5, space5); { szr_other->Add(CreateCheckBox(page_general, _("Show FPS"), wxGetTranslation(show_fps_desc), @@ -488,20 +489,28 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) wxStaticBoxSizer* const group_basic = new wxStaticBoxSizer(wxVERTICAL, page_general, _("Basic")); - group_basic->Add(szr_basic, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); - szr_general->Add(group_basic, 0, wxEXPAND | wxALL, 5); + group_basic->Add(szr_basic, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + group_basic->AddSpacer(space5); wxStaticBoxSizer* const group_display = new wxStaticBoxSizer(wxVERTICAL, page_general, _("Display")); - group_display->Add(szr_display, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); - szr_general->Add(group_display, 0, wxEXPAND | wxALL, 5); + group_display->Add(szr_display, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + group_display->AddSpacer(space5); wxStaticBoxSizer* const group_other = new wxStaticBoxSizer(wxVERTICAL, page_general, _("Other")); - group_other->Add(szr_other, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); - szr_general->Add(group_other, 0, wxEXPAND | wxALL, 5); + group_other->Add(szr_other, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + group_other->AddSpacer(space5); + + szr_general->AddSpacer(space5); + szr_general->Add(group_basic, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + szr_general->AddSpacer(space5); + szr_general->Add(group_display, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + szr_general->AddSpacer(space5); + szr_general->Add(group_other, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); } + szr_general->AddSpacer(space5); szr_general->AddStretchSpacer(); CreateDescriptionArea(page_general, szr_general); page_general->SetSizerAndFit(szr_general); @@ -514,7 +523,9 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) wxBoxSizer* const szr_enh_main = new wxBoxSizer(wxVERTICAL); // - enhancements - wxFlexGridSizer* const szr_enh = new wxFlexGridSizer(2, 5, 5); + wxGridBagSizer* const szr_enh = new wxGridBagSizer(space5, space5); + const wxGBSpan span2(1, 2); + int row = 0; // Internal resolution { @@ -540,9 +551,10 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) if (vconfig.iEFBScale > 11) choice_efbscale->SetSelection(12); - szr_enh->Add(new wxStaticText(page_enh, wxID_ANY, _("Internal Resolution:")), 1, - wxALIGN_CENTER_VERTICAL, 0); - szr_enh->Add(choice_efbscale); + szr_enh->Add(new wxStaticText(page_enh, wxID_ANY, _("Internal Resolution:")), + wxGBPosition(row, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + szr_enh->Add(choice_efbscale, wxGBPosition(row, 1), span2, wxALIGN_CENTER_VERTICAL); + row += 1; } // AA @@ -553,23 +565,25 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) PopulateAAList(); choice_aamode->Bind(wxEVT_CHOICE, &VideoConfigDiag::OnAAChanged, this); - szr_enh->Add(text_aamode, 1, wxALIGN_CENTER_VERTICAL, 0); - szr_enh->Add(choice_aamode); + szr_enh->Add(text_aamode, wxGBPosition(row, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + szr_enh->Add(choice_aamode, wxGBPosition(row, 1), span2, wxALIGN_CENTER_VERTICAL); + row += 1; } // AF { - const wxString af_choices[] = {"1x", "2x", "4x", "8x", "16x"}; - szr_enh->Add(new wxStaticText(page_enh, wxID_ANY, _("Anisotropic Filtering:")), 1, - wxALIGN_CENTER_VERTICAL, 0); - szr_enh->Add( - CreateChoice(page_enh, vconfig.iMaxAnisotropy, wxGetTranslation(af_desc), 5, af_choices)); + const std::array<wxString, 5> af_choices{{"1x", "2x", "4x", "8x", "16x"}}; + szr_enh->Add(new wxStaticText(page_enh, wxID_ANY, _("Anisotropic Filtering:")), + wxGBPosition(row, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + szr_enh->Add(CreateChoice(page_enh, vconfig.iMaxAnisotropy, wxGetTranslation(af_desc), + af_choices.size(), af_choices.data()), + wxGBPosition(row, 1), span2, wxALIGN_CENTER_VERTICAL); + row += 1; } // postproc shader if (vconfig.backend_info.bSupportsPostProcessing) { - wxFlexGridSizer* const szr_pp = new wxFlexGridSizer(3, 5, 5); choice_ppshader = new wxChoice(page_enh, wxID_ANY); RegisterControl(choice_ppshader, wxGetTranslation(ppshader_desc)); button_config_pp = new wxButton(page_enh, wxID_ANY, _("Config")); @@ -579,11 +593,11 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) choice_ppshader->Bind(wxEVT_CHOICE, &VideoConfigDiag::Event_PPShader, this); button_config_pp->Bind(wxEVT_BUTTON, &VideoConfigDiag::Event_ConfigurePPShader, this); - szr_enh->Add(new wxStaticText(page_enh, wxID_ANY, _("Post-Processing Effect:")), 1, - wxALIGN_CENTER_VERTICAL, 0); - szr_pp->Add(choice_ppshader); - szr_pp->Add(button_config_pp); - szr_enh->Add(szr_pp); + szr_enh->Add(new wxStaticText(page_enh, wxID_ANY, _("Post-Processing Effect:")), + wxGBPosition(row, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + szr_enh->Add(choice_ppshader, wxGBPosition(row, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + szr_enh->Add(button_config_pp, wxGBPosition(row, 2), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + row += 1; } else { @@ -592,31 +606,38 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) } // Scaled copy, PL, Bilinear filter - szr_enh->Add(CreateCheckBox(page_enh, _("Scaled EFB Copy"), - wxGetTranslation(scaled_efb_copy_desc), vconfig.bCopyEFBScaled)); - szr_enh->Add(CreateCheckBox(page_enh, _("Per-Pixel Lighting"), - wxGetTranslation(pixel_lighting_desc), - vconfig.bEnablePixelLighting)); - szr_enh->Add(CreateCheckBox(page_enh, _("Force Texture Filtering"), - wxGetTranslation(force_filtering_desc), vconfig.bForceFiltering)); - szr_enh->Add(CreateCheckBox(page_enh, _("Widescreen Hack"), wxGetTranslation(ws_hack_desc), - vconfig.bWidescreenHack)); - szr_enh->Add(CreateCheckBox(page_enh, _("Disable Fog"), wxGetTranslation(disable_fog_desc), - vconfig.bDisableFog)); + wxGridSizer* const cb_szr = new wxGridSizer(2, space5, space5); + cb_szr->Add(CreateCheckBox(page_enh, _("Scaled EFB Copy"), + wxGetTranslation(scaled_efb_copy_desc), vconfig.bCopyEFBScaled)); + cb_szr->Add(CreateCheckBox(page_enh, _("Per-Pixel Lighting"), + wxGetTranslation(pixel_lighting_desc), + vconfig.bEnablePixelLighting)); + cb_szr->Add(CreateCheckBox(page_enh, _("Force Texture Filtering"), + wxGetTranslation(force_filtering_desc), vconfig.bForceFiltering)); + cb_szr->Add(CreateCheckBox(page_enh, _("Widescreen Hack"), wxGetTranslation(ws_hack_desc), + vconfig.bWidescreenHack)); + cb_szr->Add(CreateCheckBox(page_enh, _("Disable Fog"), wxGetTranslation(disable_fog_desc), + vconfig.bDisableFog)); + szr_enh->Add(cb_szr, wxGBPosition(row, 0), wxGBSpan(1, 3)); + row += 1; wxStaticBoxSizer* const group_enh = new wxStaticBoxSizer(wxVERTICAL, page_enh, _("Enhancements")); - group_enh->Add(szr_enh, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); - szr_enh_main->Add(group_enh, 0, wxEXPAND | wxALL, 5); + group_enh->Add(szr_enh, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + group_enh->AddSpacer(space5); + + szr_enh_main->AddSpacer(space5); + szr_enh_main->Add(group_enh, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); // - stereoscopy if (vconfig.backend_info.bSupportsGeometryShaders) { - wxFlexGridSizer* const szr_stereo = new wxFlexGridSizer(2, 5, 5); + wxFlexGridSizer* const szr_stereo = new wxFlexGridSizer(2, space5, space5); + szr_stereo->AddGrowableCol(1); - szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Stereoscopic 3D Mode:")), 1, - wxALIGN_CENTER_VERTICAL, 0); + szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Stereoscopic 3D Mode:")), 0, + wxALIGN_CENTER_VERTICAL); const wxString stereo_choices[] = {_("Off"), _("Side-by-Side"), _("Top-and-Bottom"), _("Anaglyph"), _("Nvidia 3D Vision")}; @@ -626,37 +647,41 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) ArraySize(stereo_choices) - 1, stereo_choices); stereo_choice->Bind(wxEVT_CHOICE, &VideoConfigDiag::Event_StereoMode, this); - szr_stereo->Add(stereo_choice); + szr_stereo->Add(stereo_choice, 0, wxALIGN_CENTER_VERTICAL); - wxSlider* const sep_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoDepth, 0, 100, - wxDefaultPosition, wxDefaultSize); + DolphinSlider* const sep_slider = + new DolphinSlider(page_enh, wxID_ANY, vconfig.iStereoDepth, 0, 100, wxDefaultPosition, + FromDIP(wxSize(200, -1))); sep_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoDepth, this); RegisterControl(sep_slider, wxGetTranslation(stereo_depth_desc)); - szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Depth:")), 1, wxALIGN_CENTER_VERTICAL, - 0); - szr_stereo->Add(sep_slider, 0, wxEXPAND | wxRIGHT); + szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Depth:"))); + szr_stereo->Add(sep_slider); - conv_slider = new wxSlider(page_enh, wxID_ANY, vconfig.iStereoConvergencePercentage, 0, 200, - wxDefaultPosition, wxDefaultSize, wxSL_AUTOTICKS); + conv_slider = + new DolphinSlider(page_enh, wxID_ANY, vconfig.iStereoConvergencePercentage, 0, 200, + wxDefaultPosition, FromDIP(wxSize(200, -1)), wxSL_AUTOTICKS); conv_slider->ClearTicks(); conv_slider->SetTick(100); conv_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_StereoConvergence, this); RegisterControl(conv_slider, wxGetTranslation(stereo_convergence_desc)); - szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Convergence:")), 1, - wxALIGN_CENTER_VERTICAL, 0); - szr_stereo->Add(conv_slider, 0, wxEXPAND | wxRIGHT); + szr_stereo->Add(new wxStaticText(page_enh, wxID_ANY, _("Convergence:"))); + szr_stereo->Add(conv_slider); szr_stereo->Add(CreateCheckBox(page_enh, _("Swap Eyes"), wxGetTranslation(stereo_swap_desc), vconfig.bStereoSwapEyes)); wxStaticBoxSizer* const group_stereo = new wxStaticBoxSizer(wxVERTICAL, page_enh, _("Stereoscopy")); - group_stereo->Add(szr_stereo, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); - szr_enh_main->Add(group_stereo, 0, wxEXPAND | wxALL, 5); + group_stereo->Add(szr_stereo, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + group_stereo->AddSpacer(space5); + + szr_enh_main->AddSpacer(space5); + szr_enh_main->Add(group_stereo, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); } + szr_enh_main->AddSpacer(space5); szr_enh_main->AddStretchSpacer(); CreateDescriptionArea(page_enh, szr_enh_main); page_enh->SetSizerAndFit(szr_enh_main); @@ -674,53 +699,71 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) szr_efb->Add(CreateCheckBox(page_hacks, _("Skip EFB Access from CPU"), wxGetTranslation(efb_access_desc), vconfig.bEFBAccessEnable, true), - 0, wxBOTTOM | wxLEFT, 5); + 0, wxLEFT | wxRIGHT, space5); + szr_efb->AddSpacer(space5); szr_efb->Add(CreateCheckBox(page_hacks, _("Ignore Format Changes"), wxGetTranslation(efb_emulate_format_changes_desc), vconfig.bEFBEmulateFormatChanges, true), - 0, wxBOTTOM | wxLEFT, 5); + 0, wxLEFT | wxRIGHT, space5); + szr_efb->AddSpacer(space5); szr_efb->Add(CreateCheckBox(page_hacks, _("Store EFB Copies to Texture Only"), wxGetTranslation(skip_efb_copy_to_ram_desc), vconfig.bSkipEFBCopyToRam), - 0, wxBOTTOM | wxLEFT, 5); + 0, wxLEFT | wxRIGHT, space5); + szr_efb->AddSpacer(space5); - szr_hacks->Add(szr_efb, 0, wxEXPAND | wxALL, 5); + szr_hacks->AddSpacer(space5); + szr_hacks->Add(szr_efb, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); // Texture cache { wxStaticBoxSizer* const szr_safetex = - new wxStaticBoxSizer(wxHORIZONTAL, page_hacks, _("Texture Cache")); + new wxStaticBoxSizer(wxVERTICAL, page_hacks, _("Texture Cache")); - // TODO: Use wxSL_MIN_MAX_LABELS or wxSL_VALUE_LABEL with wx 2.9.1 - wxSlider* const stc_slider = new wxSlider(page_hacks, wxID_ANY, 0, 0, 2, wxDefaultPosition, - wxDefaultSize, wxSL_HORIZONTAL | wxSL_BOTTOM); + int slider_pos = -1; + if (vconfig.iSafeTextureCache_ColorSamples == 0) + slider_pos = 0; + else if (vconfig.iSafeTextureCache_ColorSamples == 512) + slider_pos = 1; + else if (vconfig.iSafeTextureCache_ColorSamples == 128) + slider_pos = 2; + + DolphinSlider* const stc_slider = + new DolphinSlider(page_hacks, wxID_ANY, std::max(slider_pos, 0), 0, 2, wxDefaultPosition, + wxDefaultSize, wxSL_HORIZONTAL | wxSL_BOTTOM); stc_slider->Bind(wxEVT_SLIDER, &VideoConfigDiag::Event_Stc, this); RegisterControl(stc_slider, wxGetTranslation(stc_desc)); - if (vconfig.iSafeTextureCache_ColorSamples == 0) - stc_slider->SetValue(0); - else if (vconfig.iSafeTextureCache_ColorSamples == 512) - stc_slider->SetValue(1); - else if (vconfig.iSafeTextureCache_ColorSamples == 128) - stc_slider->SetValue(2); - else - stc_slider->Disable(); // Using custom number of samples; TODO: Inform the user why this is - // disabled.. + wxBoxSizer* const slide_szr = new wxBoxSizer(wxHORIZONTAL); + slide_szr->Add(new wxStaticText(page_hacks, wxID_ANY, _("Accuracy:")), 0, + wxALIGN_CENTER_VERTICAL); + slide_szr->AddStretchSpacer(1); + slide_szr->Add(new wxStaticText(page_hacks, wxID_ANY, _("Safe")), 0, + wxALIGN_CENTER_VERTICAL | wxLEFT, space5); + slide_szr->Add(stc_slider, 2, wxALIGN_CENTER_VERTICAL); + slide_szr->Add(new wxStaticText(page_hacks, wxID_ANY, _("Fast")), 0, wxALIGN_CENTER_VERTICAL); - szr_safetex->Add(new wxStaticText(page_hacks, wxID_ANY, _("Accuracy:")), 0, wxALL, 5); - szr_safetex->AddStretchSpacer(1); - szr_safetex->Add(new wxStaticText(page_hacks, wxID_ANY, _("Safe")), 0, - wxLEFT | wxTOP | wxBOTTOM, 5); - szr_safetex->Add(stc_slider, 2, wxRIGHT, 0); - szr_safetex->Add(new wxStaticText(page_hacks, wxID_ANY, _("Fast")), 0, - wxRIGHT | wxTOP | wxBOTTOM, 5); - szr_hacks->Add(szr_safetex, 0, wxEXPAND | wxALL, 5); + szr_safetex->Add(slide_szr, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + if (slider_pos == -1) + { + stc_slider->Disable(); + wxString msg = wxString::Format(_("Hash tap count is set to %d which is non-standard.\n" + "You will need to edit the INI manually."), + vconfig.iSafeTextureCache_ColorSamples); + szr_safetex->AddSpacer(space5); + szr_safetex->Add(new wxStaticText(page_hacks, wxID_ANY, msg), 0, + wxALIGN_RIGHT | wxLEFT | wxRIGHT, space5); + } + szr_safetex->AddSpacer(space5); + + szr_hacks->AddSpacer(space5); + szr_hacks->Add(szr_safetex, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); } // - XFB { wxStaticBoxSizer* const group_xfb = - new wxStaticBoxSizer(wxHORIZONTAL, page_hacks, _("External Frame Buffer (XFB)")); + new wxStaticBoxSizer(wxVERTICAL, page_hacks, _("External Frame Buffer (XFB)")); SettingCheckBox* disable_xfb = CreateCheckBox( page_hacks, _("Disable"), wxGetTranslation(xfb_desc), vconfig.bUseXFB, true); @@ -729,16 +772,22 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) real_xfb = CreateRadioButton(page_hacks, _("Real"), wxGetTranslation(xfb_real_desc), vconfig.bUseRealXFB); - group_xfb->Add(disable_xfb, 0, wxLEFT | wxRIGHT | wxBOTTOM, 5); - group_xfb->AddStretchSpacer(1); - group_xfb->Add(virtual_xfb, 0, wxRIGHT, 5); - group_xfb->Add(real_xfb, 0, wxRIGHT, 5); - szr_hacks->Add(group_xfb, 0, wxEXPAND | wxALL, 5); + wxBoxSizer* const szr = new wxBoxSizer(wxHORIZONTAL); + szr->Add(disable_xfb, 0, wxALIGN_CENTER_VERTICAL); + szr->AddStretchSpacer(1); + szr->Add(virtual_xfb, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); + szr->Add(real_xfb, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, space5); + + group_xfb->Add(szr, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + group_xfb->AddSpacer(space5); + + szr_hacks->AddSpacer(space5); + szr_hacks->Add(group_xfb, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); } // xfb // - other hacks { - wxGridSizer* const szr_other = new wxGridSizer(2, 5, 5); + wxGridSizer* const szr_other = new wxGridSizer(2, space5, space5); szr_other->Add(CreateCheckBox(page_hacks, _("Fast Depth Calculation"), wxGetTranslation(fast_depth_calc_desc), vconfig.bFastDepthCalc)); @@ -748,10 +797,14 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) wxStaticBoxSizer* const group_other = new wxStaticBoxSizer(wxVERTICAL, page_hacks, _("Other")); - group_other->Add(szr_other, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); - szr_hacks->Add(group_other, 0, wxEXPAND | wxALL, 5); + group_other->Add(szr_other, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + group_other->AddSpacer(space5); + + szr_hacks->AddSpacer(space5); + szr_hacks->Add(group_other, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); } + szr_hacks->AddSpacer(space5); szr_hacks->AddStretchSpacer(); CreateDescriptionArea(page_hacks, szr_hacks); page_hacks->SetSizerAndFit(szr_hacks); @@ -765,7 +818,7 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) // - debug { - wxGridSizer* const szr_debug = new wxGridSizer(2, 5, 5); + wxGridSizer* const szr_debug = new wxGridSizer(2, space5, space5); szr_debug->Add(CreateCheckBox(page_advanced, _("Enable Wireframe"), wxGetTranslation(wireframe_desc), vconfig.bWireFrame)); @@ -779,13 +832,16 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) wxStaticBoxSizer* const group_debug = new wxStaticBoxSizer(wxVERTICAL, page_advanced, _("Debugging")); - szr_advanced->Add(group_debug, 0, wxEXPAND | wxALL, 5); - group_debug->Add(szr_debug, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + group_debug->Add(szr_debug, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + group_debug->AddSpacer(space5); + + szr_advanced->AddSpacer(space5); + szr_advanced->Add(group_debug, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); } // - utility { - wxGridSizer* const szr_utility = new wxGridSizer(2, 5, 5); + wxGridSizer* const szr_utility = new wxGridSizer(2, space5, space5); szr_utility->Add(CreateCheckBox(page_advanced, _("Dump Textures"), wxGetTranslation(dump_textures_desc), vconfig.bDumpTextures)); @@ -807,13 +863,16 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) wxStaticBoxSizer* const group_utility = new wxStaticBoxSizer(wxVERTICAL, page_advanced, _("Utility")); - szr_advanced->Add(group_utility, 0, wxEXPAND | wxALL, 5); - group_utility->Add(szr_utility, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + group_utility->Add(szr_utility, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + group_utility->AddSpacer(space5); + + szr_advanced->AddSpacer(space5); + szr_advanced->Add(group_utility, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); } // - misc { - wxGridSizer* const szr_misc = new wxGridSizer(2, 5, 5); + wxGridSizer* const szr_misc = new wxGridSizer(2, space5, space5); szr_misc->Add( CreateCheckBox(page_advanced, _("Crop"), wxGetTranslation(crop_desc), vconfig.bCrop)); @@ -844,24 +903,34 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) wxStaticBoxSizer* const group_misc = new wxStaticBoxSizer(wxVERTICAL, page_advanced, _("Misc")); - szr_advanced->Add(group_misc, 0, wxEXPAND | wxALL, 5); - group_misc->Add(szr_misc, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + group_misc->Add(szr_misc, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + group_misc->AddSpacer(space5); + + szr_advanced->AddSpacer(space5); + szr_advanced->Add(group_misc, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); } + szr_advanced->AddSpacer(space5); szr_advanced->AddStretchSpacer(); CreateDescriptionArea(page_advanced, szr_advanced); page_advanced->SetSizerAndFit(szr_advanced); } - wxButton* const btn_close = new wxButton(this, wxID_OK, _("Close")); - btn_close->Bind(wxEVT_BUTTON, &VideoConfigDiag::Event_ClickClose, this); + wxStdDialogButtonSizer* btn_sizer = CreateStdDialogButtonSizer(wxOK | wxNO_DEFAULT); + btn_sizer->GetAffirmativeButton()->SetLabel(_("Close")); + SetEscapeId(wxID_OK); // Escape key or window manager 'X' - Bind(wxEVT_CLOSE_WINDOW, &VideoConfigDiag::Event_Close, this); + Bind(wxEVT_BUTTON, &VideoConfigDiag::Event_Close, this, wxID_OK); wxBoxSizer* const szr_main = new wxBoxSizer(wxVERTICAL); - szr_main->Add(notebook, 1, wxEXPAND | wxALL, 5); - szr_main->Add(btn_close, 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 5); + szr_main->AddSpacer(space5); + szr_main->Add(notebook, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + szr_main->AddSpacer(space5); + szr_main->Add(btn_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + szr_main->AddSpacer(space5); + SetLayoutAdaptationMode(wxDIALOG_ADAPTATION_MODE_ENABLED); + SetLayoutAdaptationLevel(wxDIALOG_ADAPTATION_STANDARD_SIZER); SetSizerAndFit(szr_main); Center(); SetFocus(); @@ -871,8 +940,18 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) void VideoConfigDiag::Event_DisplayResolution(wxCommandEvent& ev) { - SConfig::GetInstance().strFullscreenResolution = - WxStrToStr(choice_display_resolution->GetStringSelection()); + // "Auto" has been translated, it needs to be the English string "Auto" to work + switch (choice_display_resolution->GetSelection()) + { + case 0: + SConfig::GetInstance().strFullscreenResolution = "Auto"; + break; + case wxNOT_FOUND: + break; // Nothing is selected. + default: + SConfig::GetInstance().strFullscreenResolution = + WxStrToStr(choice_display_resolution->GetStringSelection()); + } #if defined(HAVE_XRANDR) && HAVE_XRANDR main_frame->m_XRRConfig->Update(); #endif @@ -936,43 +1015,43 @@ void VideoConfigDiag::Evt_EnterControl(wxMouseEvent& ev) // look up the description of the selected control and assign it to the current description text // object's label descr_text->SetLabel(ctrl_descs[ctrl]); - descr_text->Wrap(descr_text->GetContainingSizer()->GetSize().x - 20); + descr_text->Wrap(descr_text->GetSize().GetWidth()); ev.Skip(); } -// TODO: Don't hardcode the size of the description area via line breaks -#define DEFAULT_DESC_TEXT \ - _("Move the mouse pointer over an option to display a detailed description.\n\n\n\n\n\n\n") void VideoConfigDiag::Evt_LeaveControl(wxMouseEvent& ev) { // look up description text control and reset its label - wxWindow* ctrl = (wxWindow*)ev.GetEventObject(); + wxWindow* ctrl = static_cast<wxWindow*>(ev.GetEventObject()); if (!ctrl) return; wxStaticText* descr_text = desc_texts[ctrl->GetParent()]; if (!descr_text) return; - descr_text->SetLabel(DEFAULT_DESC_TEXT); - descr_text->Wrap(descr_text->GetContainingSizer()->GetSize().x - 20); + descr_text->SetLabel(wxGetTranslation(default_desc)); + descr_text->Wrap(descr_text->GetSize().GetWidth()); + ev.Skip(); } void VideoConfigDiag::CreateDescriptionArea(wxPanel* const page, wxBoxSizer* const sizer) { + const int space5 = FromDIP(5); + // Create description frame wxStaticBoxSizer* const desc_sizer = new wxStaticBoxSizer(wxVERTICAL, page, _("Description")); - sizer->Add(desc_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + sizer->Add(desc_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sizer->AddSpacer(space5); - // Need to call SetSizerAndFit here, since we don't want the description texts to change the - // dialog width - page->SetSizerAndFit(sizer); - - // Create description text - wxStaticText* const desc_text = new wxStaticText(page, wxID_ANY, DEFAULT_DESC_TEXT); - desc_text->Wrap(desc_sizer->GetSize().x - 20); - desc_sizer->Add(desc_text, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5); + // Create description text (220 = 75*4 (75 chars), 80 = 10*8 (10 lines)) + wxStaticText* const desc_text = + new wxStaticText(page, wxID_ANY, wxGetTranslation(default_desc), wxDefaultPosition, + wxDLG_UNIT(this, wxSize(220, 80)), wxST_NO_AUTORESIZE); + desc_text->Wrap(desc_text->GetMinWidth()); + desc_sizer->Add(desc_text, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + desc_sizer->AddSpacer(space5); // Store description text object for later lookup desc_texts.emplace(page, desc_text); diff --git a/Source/Core/DolphinWX/VideoConfigDiag.h b/Source/Core/DolphinWX/VideoConfigDiag.h index cc07c62ca6..dcff2803e2 100644 --- a/Source/Core/DolphinWX/VideoConfigDiag.h +++ b/Source/Core/DolphinWX/VideoConfigDiag.h @@ -21,6 +21,7 @@ #include "Common/SysConf.h" #include "Core/ConfigManager.h" #include "Core/Core.h" +#include "DolphinWX/DolphinSlider.h" #include "DolphinWX/PostProcessingConfigDiag.h" #include "DolphinWX/WxUtils.h" #include "VideoCommon/PostProcessing.h" @@ -100,8 +101,7 @@ protected: wxMessageBox(_("Software rendering is an order of magnitude slower than using the " "other backends.\nIt's only useful for debugging purposes.\nDo you " "really want to enable software rendering? If unsure, select 'No'."), - _("Warning"), wxYES_NO | wxNO_DEFAULT | wxICON_EXCLAMATION, - wxWindow::FindFocus())); + _("Warning"), wxYES_NO | wxNO_DEFAULT | wxICON_EXCLAMATION, this)); } if (do_switch) @@ -198,8 +198,7 @@ protected: ev.Skip(); } - void Event_ClickClose(wxCommandEvent&); - void Event_Close(wxCloseEvent&); + void Event_Close(wxCommandEvent&); // Enables/disables UI elements depending on current config void OnUpdateUI(wxUpdateUIEvent& ev) @@ -274,7 +273,7 @@ protected: wxStaticText* text_aamode; wxChoice* choice_aamode; - wxSlider* conv_slider; + DolphinSlider* conv_slider; wxStaticText* label_display_resolution; diff --git a/Source/Core/DolphinWX/WxUtils.cpp b/Source/Core/DolphinWX/WxUtils.cpp index c9449df3f8..ef22a09d43 100644 --- a/Source/Core/DolphinWX/WxUtils.cpp +++ b/Source/Core/DolphinWX/WxUtils.cpp @@ -2,21 +2,36 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include <algorithm> +#include <array> +#include <cmath> #include <string> #include <wx/app.h> #include <wx/bitmap.h> +#include <wx/choice.h> +#include <wx/combo.h> +#include <wx/combobox.h> +#include <wx/display.h> #include <wx/gdicmn.h> #include <wx/image.h> #include <wx/msgdlg.h> -#include <wx/mstream.h> +#include <wx/sizer.h> +#include <wx/spinctrl.h> #include <wx/toolbar.h> +#include <wx/toplevel.h> #include <wx/utils.h> #include "Common/CommonPaths.h" #include "Common/FileUtil.h" +#include "Common/StringUtil.h" +#include "Core/ConfigManager.h" #include "DolphinWX/WxUtils.h" +#ifdef _WIN32 +#include <Windows.h> +#endif + namespace WxUtils { // Launch a file according to its mime type @@ -55,45 +70,10 @@ void ShowErrorDialog(const wxString& error_msg) wxMessageBox(error_msg, _("Error"), wxOK | wxICON_ERROR); } -wxBitmap LoadResourceBitmap(const std::string& name, const wxSize& padded_size) -{ - const std::string path_base = File::GetSysDirectory() + RESOURCES_DIR + DIR_SEP + name; - std::string path = path_base + ".png"; - double scale_factor = 1.0; -#ifdef __APPLE__ - if (wxTheApp->GetTopWindow()->GetContentScaleFactor() >= 2) - { - const std::string path_2x = path_base + "@2x.png"; - if (File::Exists(path_2x)) - { - path = path_2x; - scale_factor = 2.0; - } - } -#endif - wxImage image(StrToWxStr(path), wxBITMAP_TYPE_PNG); - - if (padded_size != wxSize()) - { - // Add padding if necessary (or crop, but images aren't supposed to be large enough to require - // that). - // The image will be left-aligned and vertically centered. - const wxSize scaled_padded_size = padded_size * scale_factor; - image.Resize(scaled_padded_size, - wxPoint(0, (scaled_padded_size.GetHeight() - image.GetHeight()) / 2)); - } - -#ifdef __APPLE__ - return wxBitmap(image, -1, scale_factor); -#else - return wxBitmap(image); -#endif -} - wxBitmap CreateDisabledButtonBitmap(const wxBitmap& original) { wxImage image = original.ConvertToImage(); - return wxBitmap(image.ConvertToDisabled(240)); + return wxBitmap(image.ConvertToDisabled(240), wxBITMAP_SCREEN_DEPTH, original.GetScaleFactor()); } void AddToolbarButton(wxToolBar* toolbar, int toolID, const wxString& label, const wxBitmap& bitmap, @@ -105,6 +85,440 @@ void AddToolbarButton(wxToolBar* toolbar, int toolID, const wxString& label, con wxITEM_NORMAL, shortHelp); } +wxIconBundle GetDolphinIconBundle() +{ + static wxIconBundle s_bundle; + if (!s_bundle.IsEmpty()) + return s_bundle; + +#ifdef _WIN32 + + // Convert the Windows ICO file into a wxIconBundle by tearing it apart into each individual + // sub-icon using the Win32 API. This is necessary because WX uses its own wxIcons internally + // which (unlike QIcon in Qt) only contain 1 image per icon, hence why wxIconBundle exists. + HINSTANCE dolphin = GetModuleHandleW(nullptr); + for (int size : {16, 32, 48, 256}) + { + // Extract resource from embedded DolphinWX.rc + HANDLE win32_icon = + LoadImageW(dolphin, L"\"DOLPHIN\"", IMAGE_ICON, size, size, LR_CREATEDIBSECTION); + if (win32_icon && win32_icon != INVALID_HANDLE_VALUE) + { + wxIcon icon; + icon.CreateFromHICON(reinterpret_cast<HICON>(win32_icon)); + s_bundle.AddIcon(icon); + } + } + +#else + + for (const char* fname : {"Dolphin.png", "dolphin_logo.png", "dolphin_logo@2x.png"}) + { + wxImage image{StrToWxStr(File::GetSysDirectory() + RESOURCES_DIR DIR_SEP + fname), + wxBITMAP_TYPE_PNG}; + if (image.IsOk()) + { + wxIcon icon; + icon.CopyFromBitmap(image); + s_bundle.AddIcon(icon); + } + } + +#endif + + return s_bundle; +} + +wxRect GetVirtualScreenGeometry() +{ + wxRect geometry; + for (unsigned int i = 0, end = wxDisplay::GetCount(); i < end; ++i) + geometry.Union(wxDisplay(i).GetGeometry()); + return geometry; +} + +void SetWindowSizeAndFitToScreen(wxTopLevelWindow* tlw, wxPoint pos, wxSize size, + wxSize default_size) +{ + if (tlw->IsMaximized()) + return; + + // NOTE: Positions can be negative and still be valid. Coordinates are relative to the + // primary monitor so if the primary monitor is in the middle then (-1000, 10) is a + // valid position on the monitor to the left of the primary. (This does not apply to + // sizes obviously) + wxRect screen_geometry; + wxRect window_geometry{pos, size}; + + if (wxDisplay::GetCount() > 1) + screen_geometry = GetVirtualScreenGeometry(); + else + screen_geometry = wxDisplay(0).GetClientArea(); + + // Initialize the default size if it is wxDefaultSize or otherwise negative. + default_size.DecTo(screen_geometry.GetSize()); + default_size.IncTo(tlw->GetMinSize()); + if (!default_size.IsFullySpecified()) + default_size.SetDefaults(wxDisplay(0).GetClientArea().GetSize() / 2); + + // If the position we're given doesn't make sense then go with the current position. + // (Assuming the window was created with wxDefaultPosition then this should be reasonable) + if (pos.x - screen_geometry.GetLeft() < -1000 || pos.y - screen_geometry.GetTop() < -1000 || + pos.x - screen_geometry.GetRight() > 1000 || pos.y - screen_geometry.GetBottom() > 1000) + { + window_geometry.SetPosition(tlw->GetPosition()); + } + + // If the window is bigger than all monitors combined, or negative (uninitialized) then reset it. + if (window_geometry.IsEmpty() || window_geometry.GetWidth() > screen_geometry.GetWidth() || + window_geometry.GetHeight() > screen_geometry.GetHeight()) + { + window_geometry.SetSize(default_size); + } + + // Check if the window entirely lives on a single monitor without spanning. + // If the window does not span multiple screens then we should constrain it within that + // single monitor instead of the entire virtual desktop space. + // The benefit to doing this is that we can account for the OS X menu bar and Windows task + // bar which are treated as invisible when only looking at the virtual desktop instead of + // an individual screen. + if (wxDisplay::GetCount() > 1) + { + // SPECIAL CASE: If the window is entirely outside the visible area of the desktop then we + // put it back on the primary (zero) monitor. + wxRect monitor_intersection{window_geometry}; + int the_monitor = 0; + if (!monitor_intersection.Intersect(screen_geometry).IsEmpty()) + { + std::array<int, 4> monitors{{wxDisplay::GetFromPoint(monitor_intersection.GetTopLeft()), + wxDisplay::GetFromPoint(monitor_intersection.GetTopRight()), + wxDisplay::GetFromPoint(monitor_intersection.GetBottomLeft()), + wxDisplay::GetFromPoint(monitor_intersection.GetBottomRight())}}; + the_monitor = wxNOT_FOUND; + bool intersected = false; + for (int one_monitor : monitors) + { + if (one_monitor == the_monitor || one_monitor == wxNOT_FOUND) + continue; + if (the_monitor != wxNOT_FOUND) + { + // The window is spanning multiple screens. + the_monitor = wxNOT_FOUND; + break; + } + the_monitor = one_monitor; + intersected = true; + } + // If we get wxNOT_FOUND for all corners then there are holes in the virtual desktop and the + // entire window is lost in one. (e.g. 3 monitors in an 'L', window in top-right) + if (!intersected) + the_monitor = 0; + } + if (the_monitor != wxNOT_FOUND) + { + // We'll only use the client area of this monitor if the window will actually fit. + // (It may not fit if the window is spilling off the edge so it isn't entirely visible) + wxRect client_area{wxDisplay(the_monitor).GetClientArea()}; + if (client_area.GetWidth() >= window_geometry.GetWidth() && + client_area.GetHeight() >= window_geometry.GetHeight()) + { + screen_geometry = client_area; + } + } + } + + // The window SHOULD be small enough to fit on the screen, but it might be spilling off an edge + // so we'll snap it to the nearest edge as necessary. + if (!screen_geometry.Contains(window_geometry)) + { + // NOTE: The order is important here, if the window *is* too big to fit then it will snap to + // the top-left corner. + int spill_x = std::max(0, window_geometry.GetRight() - screen_geometry.GetRight()); + int spill_y = std::max(0, window_geometry.GetBottom() - screen_geometry.GetBottom()); + window_geometry.Offset(-spill_x, -spill_y); + if (window_geometry.GetTop() < screen_geometry.GetTop()) + window_geometry.SetTop(screen_geometry.GetTop()); + if (window_geometry.GetLeft() < screen_geometry.GetLeft()) + window_geometry.SetLeft(screen_geometry.GetLeft()); + } + + tlw->SetSize(window_geometry, wxSIZE_ALLOW_MINUS_ONE); +} + +wxSizer* GiveMinSize(wxWindow* window, const wxSize& min_size) +{ + wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); + int flags = wxEXPAND; + // On Windows comboboxes will misrender when stretched vertically. + if (wxDynamicCast(window, wxChoice) || wxDynamicCast(window, wxComboBox) || + wxDynamicCast(window, wxComboCtrl)) + flags = wxALIGN_CENTER_VERTICAL; + sizer->Add(window, 1, flags); + sizer->SetMinSize(min_size); + return sizer; +} + +wxSizer* GiveMinSizeDIP(wxWindow* window, const wxSize& min_size) +{ + return GiveMinSize(window, window->FromDIP(min_size)); +} + +wxSize GetTextWidgetMinSize(const wxControl* control, const wxString& value) +{ + return control->GetSizeFromTextSize(control->GetTextExtent(value)); +} + +wxSize GetTextWidgetMinSize(const wxControl* control, unsigned int value) +{ + return GetTextWidgetMinSize(control, wxString::Format("%u", value)); +} + +wxSize GetTextWidgetMinSize(const wxControl* control, int value) +{ + return GetTextWidgetMinSize(control, wxString::Format("%d", value)); +} + +wxSize GetTextWidgetMinSize(const wxSpinCtrl* spinner) +{ + wxSize size = GetTextWidgetMinSize(spinner, spinner->GetMin()); + size.IncTo(GetTextWidgetMinSize(spinner, spinner->GetMax())); + return size; +} + +static wxImage LoadScaledImage(const std::string& file_path, const wxWindow* context, + const wxSize& output_size, const wxRect& usable_rect, LSIFlags flags, + const wxColour& fill_color) +{ + std::string fpath, fname, fext; + SplitPath(file_path, &fpath, &fname, &fext); + + const double window_scale_factor = context->GetContentScaleFactor(); + // Compute the total scale factor from the ratio of DIPs to window pixels (FromDIP) and + // window pixels to framebuffer pixels (GetContentScaleFactor). + // NOTE: Usually only one of these is meaningful: + // - On Windows/GTK2: content_scale = 1.0, FromDIP = 96DPI -> Screen DPI + // - On Mac OS X: content_scale = screen_dpi / 96, FromDIP = 96DPI -> 96DPI (no-op) + // [The 1024 is arbitrarily large to minimise rounding error, it has no significance] + const double scale_factor = (context->FromDIP(1024) / 1024.0) * window_scale_factor; + + // We search for files on quarter ratios of DIPs to framebuffer pixels. + // By default, the algorithm prefers to find an exact or bigger size then downscale if + // needed but will resort to upscaling if a bigger image cannot be found. + // E.g. A basic retina screen on Mac OS X has a scale_factor of 2.0, so we would look for + // @2x, @2.25x, @2.5x, @2.75x, @3x, @1.75x, @1.5x, @1.25x, @1x, then give up. + // (At 125% on Windows the search is @1.25, @1.5, @1.75, @2, @2.25, @1) + // If flags does not include LSI_SCALE_DOWN (i.e. we would be forced to crop big + // images instead of scaling them) then we will only accept smaller sizes, i.e. + // @2x, @1.75, @1.5, @1.25, @1, then give up. + // NOTE: We do a lot of exact comparisons against floating point here but it's fine + // because the numbers involved are all powers of 2 so can be represented exactly. + wxImage image; + double selected_image_scale = 1; + { + auto image_check = [&](double scale) -> bool { + std::string path = fpath + fname + StringFromFormat("@%gx", scale) + fext; + if (!File::Exists(path)) + { + // Special Case: @1x may not have a suffix at all. + if (scale != 1.0 || !File::Exists(file_path)) + return false; + path = file_path; + } + if (!image.LoadFile(StrToWxStr(path), wxBITMAP_TYPE_ANY)) + return false; + selected_image_scale = scale; + return true; + }; + const bool prefer_smaller = !(flags & LSI_SCALE_DOWN); + const double scale_factor_quarter = + prefer_smaller ? std::floor(scale_factor * 4) / 4 : std::ceil(scale_factor * 4) / 4; + // Search for bigger sizes first (preferred) + if (!prefer_smaller) + { + // We search within a 'circle' of the exact match limited by scale=1.0. + // i.e. scale_factor = 1.5, radius = 0.5; scale = 2.5, radius = 1.5. + // The minimum radius is 1.0. + double limit = std::max(scale_factor_quarter * 2 - 1, scale_factor_quarter + 1); + for (double quarter = scale_factor_quarter; quarter <= limit; quarter += 0.25) + { + if (image_check(quarter)) + break; + } + } + // If we didn't hit a bigger size then we'll fallback to looking for smaller ones + if (!image.IsOk()) + { + double quarter = scale_factor_quarter; + if (!prefer_smaller) // So we don't recheck the exact match + quarter -= 0.25; + for (; quarter >= 1.0; quarter -= 0.25) + { + if (image_check(quarter)) + break; + } + } + } + + // The file apparently does not exist so we give up. Create a white square placeholder instead. + if (!image.IsOk()) + { + wxLogError("Could not find resource: %s", StrToWxStr(file_path)); + image.Create(1, 1, false); + image.Clear(0xFF); + } + + return ScaleImage(image, selected_image_scale, window_scale_factor, output_size, usable_rect, + flags, fill_color); +} + +wxBitmap LoadScaledBitmap(const std::string& file_path, const wxWindow* context, + const wxSize& output_size, const wxRect& usable_rect, LSIFlags flags, + const wxColour& fill_color) +{ + return wxBitmap(LoadScaledImage(file_path, context, output_size, usable_rect, flags, fill_color), + wxBITMAP_SCREEN_DEPTH, context->GetContentScaleFactor()); +} + +wxBitmap LoadScaledResourceBitmap(const std::string& name, const wxWindow* context, + const wxSize& output_size, const wxRect& usable_rect, + LSIFlags flags, const wxColour& fill_color) +{ + std::string path = File::GetSysDirectory() + RESOURCES_DIR DIR_SEP + name + ".png"; + return LoadScaledBitmap(path, context, output_size, usable_rect, flags, fill_color); +} + +wxBitmap LoadScaledThemeBitmap(const std::string& name, const wxWindow* context, + const wxSize& output_size, const wxRect& usable_rect, LSIFlags flags, + const wxColour& fill_color) +{ + std::string path = File::GetThemeDir(SConfig::GetInstance().theme_name) + name + ".png"; + return LoadScaledBitmap(path, context, output_size, usable_rect, flags, fill_color); +} + +wxBitmap ScaleImageToBitmap(const wxImage& image, const wxWindow* context, + const wxSize& output_size, const wxRect& usable_rect, LSIFlags flags, + const wxColour& fill_color) +{ + double scale_factor = context->GetContentScaleFactor(); + return wxBitmap(ScaleImage(image, 1.0, scale_factor, output_size, usable_rect, flags, fill_color), + wxBITMAP_SCREEN_DEPTH, scale_factor); +} + +wxBitmap ScaleImageToBitmap(const wxImage& image, const wxWindow* context, double source_scale, + LSIFlags flags, const wxColour& fill_color) +{ + double scale_factor = context->GetContentScaleFactor(); + return wxBitmap(ScaleImage(image, source_scale, scale_factor, wxDefaultSize, wxDefaultSize, flags, + fill_color), + wxBITMAP_SCREEN_DEPTH, scale_factor); +} + +wxImage ScaleImage(wxImage image, double source_scale_factor, double content_scale_factor, + wxSize output_size, wxRect usable_rect, LSIFlags flags, + const wxColour& fill_color) +{ + if (!image.IsOk()) + { + wxFAIL_MSG("WxUtils::ScaleImage expects a valid image."); + return image; + } + + if (content_scale_factor != 1.0) + { + output_size *= content_scale_factor; + usable_rect.SetPosition(usable_rect.GetPosition() * content_scale_factor); + usable_rect.SetSize(usable_rect.GetSize() * content_scale_factor); + } + + // Fix the output size if it's unset. + wxSize img_size = image.GetSize(); + if (output_size.GetWidth() < 1) + output_size.SetWidth( + static_cast<int>(img_size.GetWidth() * (content_scale_factor / source_scale_factor))); + if (output_size.GetHeight() < 1) + output_size.SetHeight( + static_cast<int>(img_size.GetHeight() * (content_scale_factor / source_scale_factor))); + + // Fix the usable rect. If it's empty then the whole canvas is usable. + if (usable_rect.IsEmpty()) + { + // Constructs a temp wxRect 0,0->output_size then move assigns it. + usable_rect = output_size; + } + else if (!usable_rect.Intersects(output_size)) + { + wxFAIL_MSG("Usable Zone Rectangle is not inside the canvas. Check the output size is correct."); + image.Create(1, 1, false); + image.SetRGB(0, 0, fill_color.Red(), fill_color.Green(), fill_color.Blue()); + if (fill_color.Alpha() == wxALPHA_TRANSPARENT) + image.SetMaskColour(fill_color.Red(), fill_color.Green(), fill_color.Blue()); + usable_rect = output_size; + } + + // Step 1: Scale the image + if ((flags & LSI_SCALE) != LSI_SCALE_NONE) + { + if (flags & LSI_SCALE_NO_ASPECT) + { + // Stretch scale without preserving the aspect ratio. + bool scale_width = (img_size.GetWidth() > usable_rect.GetWidth() && flags & LSI_SCALE_DOWN) || + (img_size.GetWidth() < usable_rect.GetWidth() && flags & LSI_SCALE_UP); + bool scale_height = + (img_size.GetHeight() > usable_rect.GetHeight() && flags & LSI_SCALE_DOWN) || + (img_size.GetHeight() < usable_rect.GetHeight() && flags & LSI_SCALE_UP); + if (scale_width || scale_height) + { + // NOTE: Using BICUBIC instead of HIGH because it's the same internally + // except that downscaling uses a box filter with awful obvious aliasing + // for non-integral scale factors. + image.Rescale(scale_width ? usable_rect.GetWidth() : img_size.GetWidth(), + scale_height ? usable_rect.GetHeight() : img_size.GetHeight(), + wxIMAGE_QUALITY_BICUBIC); + } + } + else + { + // Scale while preserving the aspect ratio. + double scale = std::min(static_cast<double>(usable_rect.GetWidth()) / img_size.GetWidth(), + static_cast<double>(usable_rect.GetHeight()) / img_size.GetHeight()); + int target_width = static_cast<int>(img_size.GetWidth() * scale); + int target_height = static_cast<int>(img_size.GetHeight() * scale); + // Bilinear produces sharper images when upscaling, bicubic tends to smear/blur sharp edges. + if (scale > 1.0 && flags & LSI_SCALE_UP) + image.Rescale(target_width, target_height, wxIMAGE_QUALITY_BILINEAR); + else if (scale < 1.0 && flags & LSI_SCALE_DOWN) + image.Rescale(target_width, target_height, wxIMAGE_QUALITY_BICUBIC); + } + img_size = image.GetSize(); + } + + // Step 2: Resize the canvas to match the output size. + // NOTE: If NOT using LSI_SCALE_DOWN then this will implicitly crop the image + if (img_size != output_size || usable_rect.GetPosition() != wxPoint()) + { + wxPoint base = usable_rect.GetPosition(); + if (flags & LSI_ALIGN_HCENTER) + base.x += (usable_rect.GetWidth() - img_size.GetWidth()) / 2; + else if (flags & LSI_ALIGN_RIGHT) + base.x += usable_rect.GetWidth() - img_size.GetWidth(); + if (flags & LSI_ALIGN_VCENTER) + base.y += (usable_rect.GetHeight() - img_size.GetHeight()) / 2; + else if (flags & LSI_ALIGN_BOTTOM) + base.y += usable_rect.GetHeight() - img_size.GetHeight(); + + int r = -1, g = -1, b = -1; + if (fill_color.Alpha() != wxALPHA_TRANSPARENT) + { + r = fill_color.Red(); + g = fill_color.Green(); + b = fill_color.Blue(); + } + image.Resize(output_size, base, r, g, b); + } + + return image; +} + } // namespace std::string WxStrToStr(const wxString& str) diff --git a/Source/Core/DolphinWX/WxUtils.h b/Source/Core/DolphinWX/WxUtils.h index 1f7937e819..6cb449ae2d 100644 --- a/Source/Core/DolphinWX/WxUtils.h +++ b/Source/Core/DolphinWX/WxUtils.h @@ -5,11 +5,18 @@ #pragma once #include <string> +#include <wx/colour.h> #include <wx/gdicmn.h> #include <wx/string.h> +class wxControl; class wxBitmap; +class wxImage; +class wxSizer; +class wxSpinCtrl; class wxToolBar; +class wxTopLevelWindow; +class wxWindow; namespace WxUtils { @@ -22,9 +29,6 @@ void Explore(const std::string& path); // Displays a wxMessageBox geared for errors void ShowErrorDialog(const wxString& error_msg); -// Reads a PNG from the Resources folder -wxBitmap LoadResourceBitmap(const std::string& name, const wxSize& padded_size = wxSize()); - // From a wxBitmap, creates the corresponding disabled version for toolbar buttons wxBitmap CreateDisabledButtonBitmap(const wxBitmap& original); @@ -32,6 +36,111 @@ wxBitmap CreateDisabledButtonBitmap(const wxBitmap& original); void AddToolbarButton(wxToolBar* toolbar, int toolID, const wxString& label, const wxBitmap& bitmap, const wxString& shortHelp); +// Gets a complete set of window icons at all relevant sizes, use with wxTopLevelWindow::SetIcons +wxIconBundle GetDolphinIconBundle(); + +// Get the dimensions of the virtual desktop that spans all monitors. +// Matches GetSystemMetrics(SM_XVIRTUALSCREEN), etc on Windows. +wxRect GetVirtualScreenGeometry(); + +// Takes a top-level window and resizes / repositions it so it fits on the screen. +// Supports spanning multiple monitors if there are multiple monitors. +// Will snap to edge if the window is small enough to fit but spills over the boundary. +void SetWindowSizeAndFitToScreen(wxTopLevelWindow* tlw, wxPoint pos, wxSize size, + wxSize default_size = wxDefaultSize); + +// wxSizers use the minimum size of a widget when computing layout instead of the best size. +// The best size is only taken when the minsize is -1,-1 (i.e. undefined). +// This means that elements with a MinSize specified will always have that exact size unless +// wxEXPAND-ed. +// This problem can be resolved by wrapping the widget in a sizer and setting the minimum size on +// the sizer instead. Sizers will always use the best size of the widget, treating their own MinSize +// as a floor which is usually what you want. +wxSizer* GiveMinSize(wxWindow* window, const wxSize& min_size); +wxSizer* GiveMinSizeDIP(wxWindow* window, const wxSize& min_size); + +// Compute the proper size for a text widget (wxTextCtrl, wxChoice, wxSpinCtrl, etc) +// Based on the text it will be required to hold. This gives the element the minimum +// width to hold the largest text value instead of being arbitrarily wide. +wxSize GetTextWidgetMinSize(const wxControl* control, const wxString& value); +wxSize GetTextWidgetMinSize(const wxControl* control, unsigned int value); +wxSize GetTextWidgetMinSize(const wxControl* control, int value); +wxSize GetTextWidgetMinSize(const wxSpinCtrl* spinner); + +enum LSIFlags : unsigned int +{ + LSI_SCALE_NONE = 0, // Disable scaling, only resize canvas + LSI_SCALE_UP = 1, // Scale up if needed, but crop instead of scaling down + LSI_SCALE_DOWN = 2, // Scale down if needed, only expand canvas instead of scaling up + LSI_SCALE = LSI_SCALE_UP | LSI_SCALE_DOWN, // Scale either way as needed. + LSI_SCALE_NO_ASPECT = 8, // Disable preserving the aspect ratio of the image. + + LSI_ALIGN_LEFT = 0, // Place image at the left edge of canvas + LSI_ALIGN_RIGHT = 0x10, // Place image at the right edge of canvas + LSI_ALIGN_HCENTER = 0x20, // Place image in the horizontal center of canvas + LSI_ALIGN_TOP = 0, // Place image at the top of the canvas + LSI_ALIGN_BOTTOM = 0x40, // Place image at the bottom of the canvas + LSI_ALIGN_VCENTER = 0x80, // Place image in the vertical center of canvas + + LSI_ALIGN_CENTER = LSI_ALIGN_HCENTER | LSI_ALIGN_VCENTER, + + LSI_DEFAULT = LSI_SCALE | LSI_ALIGN_CENTER +}; +constexpr LSIFlags operator|(LSIFlags left, LSIFlags right) +{ + return static_cast<LSIFlags>(static_cast<unsigned int>(left) | right); +} +constexpr LSIFlags operator&(LSIFlags left, LSIFlags right) +{ + return static_cast<LSIFlags>(static_cast<unsigned int>(left) & right); +} + +// Swiss army knife loader function for preparing a scaled resource image file. +// Only the path and context are mandatory, other parameters can be ignored. +// NOTE: All size parameters are in window pixels, not DIPs or framebuffer pixels. +// output_size = size of image canvas if different from native image size. E.g. 96x32 +// usable_rect = part of image canvas that is considered usable. E.g. 0,0 -> 32,32 +// Usable zone is helpful if the canvas is bigger than the area which will be drawn on screen. +// flags = See LSIFlags +// fill_color = Color to fill the unused canvas area (due to aspect ratio or usable_rect). +wxBitmap LoadScaledBitmap(const std::string& file_path, const wxWindow* context, + const wxSize& output_size = wxDefaultSize, + const wxRect& usable_rect = wxDefaultSize, LSIFlags flags = LSI_DEFAULT, + const wxColour& fill_color = wxTransparentColour); +wxBitmap LoadScaledResourceBitmap(const std::string& name, const wxWindow* context, + const wxSize& output_size = wxDefaultSize, + const wxRect& usable_rect = wxDefaultSize, + LSIFlags flags = LSI_DEFAULT, + const wxColour& fill_color = wxTransparentColour); +wxBitmap LoadScaledThemeBitmap(const std::string& name, const wxWindow* context, + const wxSize& output_size = wxDefaultSize, + const wxRect& usable_rect = wxDefaultSize, + LSIFlags flags = LSI_DEFAULT, + const wxColour& fill_color = wxTransparentColour); + +// Variant of LoadScaledBitmap to scale an image that didn't come from a file. +wxBitmap ScaleImageToBitmap(const wxImage& image, const wxWindow* context, + const wxSize& output_size = wxDefaultSize, + const wxRect& usable_rect = wxDefaultSize, LSIFlags flags = LSI_DEFAULT, + const wxColour& fill_color = wxTransparentColour); + +// Rescales image to screen DPI. +// "Source scale" is essentially the image's DPI as a ratio to 96DPI, e.g. 144DPI image has a +// scale of 1.5. +wxBitmap ScaleImageToBitmap(const wxImage& image, const wxWindow* context, double source_scale, + LSIFlags flags = LSI_DEFAULT, + const wxColour& fill_color = wxTransparentColour); + +// Internal scaling engine behind all the Scaling functions. +// Exposes all control parameters instead of infering them from other sources. +// "Content scale" is a factor applied to output_size and usable_rect internally to convert them +// to framebuffer pixel sizes. +// NOTE: Source scale factor only matters if you don't explicitly specify the output size. +wxImage ScaleImage(wxImage image, double source_scale_factor = 1.0, + double content_scale_factor = 1.0, wxSize output_size = wxDefaultSize, + wxRect usable_rect = wxDefaultSize, LSIFlags flags = LSI_DEFAULT, + const wxColour& fill_color = wxTransparentColour); + } // namespace std::string WxStrToStr(const wxString& str);