Add all the files

This commit is contained in:
Exzap 2022-08-22 22:21:23 +02:00
parent e3db07a16a
commit d60742f52b
1445 changed files with 430238 additions and 0 deletions

View file

@ -0,0 +1,317 @@
#include "gui/wxgui.h"
#include "TextList.h"
#include <wx/setup.h>
#include <wx/tooltip.h>
TextList::~TextList()
{
m_tooltip_timer->Stop();
this->Unbind(wxEVT_MOTION, &TextList::OnMouseMoveEvent, this);
this->Unbind(wxEVT_KEY_DOWN, &TextList::OnKeyDownEvent, this);
this->Unbind(wxEVT_KEY_UP, &TextList::OnKeyUpEvent, this);
this->Unbind(wxEVT_PAINT, &TextList::OnPaintEvent, this);
this->Unbind(wxEVT_LEFT_DOWN, &TextList::OnMouseDownEvent, this);
this->Unbind(wxEVT_LEFT_UP, &TextList::OnMouseUpEvent, this);
this->Unbind(wxEVT_LEFT_DCLICK, &TextList::OnMouseDClickEvent, this);
this->Unbind(wxEVT_CONTEXT_MENU, &TextList::OnContextMenu, this);
this->Unbind(wxEVT_ERASE_BACKGROUND, &TextList::OnEraseBackground, this);
}
TextList::TextList(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
: wxControl(parent, id, pos, size, style), wxScrollHelper(this)
{
m_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
wxWindowBase::SetBackgroundStyle(wxBG_STYLE_PAINT);
wxClientDC dc(this);
m_line_height = dc.GetCharHeight();
m_char_width = dc.GetCharWidth();
m_yScrollPixelsPerLine = m_line_height;
this->ShowScrollbars(wxSHOW_SB_DEFAULT, wxSHOW_SB_DEFAULT);
m_tooltip = new wxToolTip(wxEmptyString);
this->Bind(wxEVT_MOTION, &TextList::OnMouseMoveEvent, this);
this->Bind(wxEVT_KEY_DOWN, &TextList::OnKeyDownEvent, this);
this->Bind(wxEVT_KEY_UP, &TextList::OnKeyUpEvent, this);
this->Bind(wxEVT_PAINT, &TextList::OnPaintEvent, this);
this->Bind(wxEVT_LEFT_DOWN, &TextList::OnMouseDownEvent, this);
this->Bind(wxEVT_LEFT_UP, &TextList::OnMouseUpEvent, this);
this->Bind(wxEVT_LEFT_DCLICK, &TextList::OnMouseDClickEvent, this);
this->Bind(wxEVT_CONTEXT_MENU, &TextList::OnContextMenu, this);
this->Bind(wxEVT_ERASE_BACKGROUND, &TextList::OnEraseBackground, this);
m_tooltip_window = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER);
m_tooltip_window->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK));
m_tooltip_window->Hide();
m_tooltip_timer = new wxTimer(this);
this->Bind(wxEVT_TIMER, &TextList::OnTooltipTimer, this);
}
void TextList::RefreshControl(const wxRect* update_region)
{
if(update_region)
Refresh(true, update_region);
else
{
wxRect region = GetClientRect();
update_region = &region;
Refresh(true, update_region);
}
}
void TextList::RefreshLine(uint32 line)
{
wxRect update_region = GetClientRect();
update_region.y = (sint32)line * m_line_height;
update_region.height = m_line_height;
CalcScrolledPosition(0, update_region.y, nullptr, &update_region.y);
// debug_printf("update: <%x, %x>\n", update_region.y, update_region.height);
Refresh(true, &update_region);
}
wxSize TextList::DoGetVirtualSize() const
{
return {wxDefaultCoord, (int)(m_element_count + 1) * m_line_height};
}
void TextList::DoSetSize(int x, int y, int width, int height, int sizeFlags)
{
wxControl::DoSetSize(x, y, width, height, sizeFlags);
m_elements_visible = (height + m_line_height - 1) / m_line_height;
Refresh();
}
void TextList::SetScrollPos(int orient, int pos, bool refresh)
{
wxControl::SetScrollPos(orient, pos, refresh);
}
void TextList::DrawLineBackground(wxDC& dc, uint32 line, const wxColour& colour, uint32 lines) const
{
wxRect rect;
rect.x = GetPosition().x;
rect.y = line * m_line_height;
rect.width = GetSize().x;
rect.height = m_line_height * lines;
dc.SetBrush(colour);
dc.DrawRectangle(rect);
}
void TextList::DrawLineBackground(wxDC& dc, const wxPoint& position, const wxColour& colour, uint32 lines) const
{
wxRect rect;
rect.x = position.x;
rect.y = position.y;
rect.width = this->GetSize().x;
rect.height = m_line_height * lines;
dc.SetBrush(colour);
dc.DrawRectangle(rect);
}
bool TextList::SetElementCount(size_t element_count)
{
if (m_element_count == element_count)
return false;
if (element_count > 0x7FFFFFFF)
element_count = 0x7FFFFFFF;
m_element_count = element_count;
this->AdjustScrollbars();
return true;
}
uint32 TextList::GetElementCount() const
{
return m_element_count;
}
bool TextList::IsKeyDown(sint32 keycode)
{
return m_key_states[keycode];
}
void TextList::WriteText(wxDC& dc, const wxString& text, wxPoint& position) const
{
dc.ResetBoundingBox();
dc.DrawText(text, position);
position.x += dc.MaxX() - dc.MinX();
}
void TextList::WriteText(wxDC& dc, const wxString& text, wxPoint& position, const wxColour& color) const
{
dc.SetTextForeground(color);
WriteText(dc, text, position);
}
void TextList::NextLine(wxPoint& position, const wxPoint* start_position) const
{
position.y += m_line_height;
if (start_position)
position.x = start_position->x;
}
void TextList::OnMouseDown() {}
void TextList::OnMouseUp() {}
bool TextList::OnShowTooltip(const wxPoint& position, uint32 line)
{
return false;
}
void TextList::OnMouseMoveEvent(wxMouseEvent& event)
{
m_tooltip_timer->Stop();
m_tooltip_timer->StartOnce(250);
wxPoint position = event.GetPosition();
CalcUnscrolledPosition(position.x, position.y, &position.x, &position.y);
m_mouse_position = position;
if(m_mouse_down)
m_selection.SetBottomRight(position);
const sint32 line = position.y / m_line_height;
OnMouseMove(position, line);
}
void TextList::OnKeyDownEvent(wxKeyEvent& event)
{
const auto key_code = event.GetKeyCode();
const auto it = m_key_states.find(key_code);
if(it == m_key_states.end() || !it->second)
{
m_key_states[key_code] = true;
OnKeyPressed(key_code, event.GetPosition());
}
event.Skip();
}
void TextList::OnKeyUpEvent(wxKeyEvent& event)
{
m_key_states[event.GetKeyCode()] = false;
}
void TextList::OnMouseDownEvent(wxMouseEvent& event)
{
m_mouse_down = true;
wxPoint position = event.GetPosition();
CalcUnscrolledPosition(position.x, position.y, &position.x, &position.y);
m_selection.SetPosition(position);
m_selection.SetBottomRight(position);
OnMouseDown();
event.Skip();
}
void TextList::OnMouseUpEvent(wxMouseEvent& event)
{
m_mouse_down = false;
OnMouseUp();
event.Skip();
}
void TextList::OnMouseDClickEvent(wxMouseEvent& event)
{
wxPoint position = event.GetPosition();
CalcUnscrolledPosition(position.x, position.y, &position.x, &position.y);
m_selection.SetPosition(position);
m_selection.SetBottomRight(position);
const uint32 line = position.y / m_line_height;
OnMouseDClick(position, line);
}
void TextList::OnContextMenu(wxContextMenuEvent& event)
{
wxPoint position = event.GetPosition();
if (position == wxDefaultPosition)
return;
wxPoint clientPosition = ScreenToClient(position);
CalcUnscrolledPosition(clientPosition.x, clientPosition.y, &clientPosition.x, &clientPosition.y);
m_selection.SetPosition(clientPosition);
m_selection.SetBottomRight(clientPosition);
const uint32 line = clientPosition.y / m_line_height;
OnContextMenu(clientPosition, line);
}
void TextList::OnTooltipTimer(wxTimerEvent& event)
{
m_tooltip_window->Hide();
const auto cursor_position = wxGetMousePosition();
const auto position = GetScreenPosition();
if (cursor_position.x < position.x || cursor_position.y < position.y)
return;
const auto size = GetSize();
if (position.x + size.x < cursor_position.x || position.y + size.y < cursor_position.y)
return;
const sint32 line = position.y / m_line_height;
if(OnShowTooltip(m_mouse_position, line))
{
m_tooltip_window->SetPosition(wxPoint(m_mouse_position.x + 15, m_mouse_position.y + 15));
m_tooltip_window->SendSizeEvent();
m_tooltip_window->Show();
}
}
void TextList::OnPaintEvent(wxPaintEvent& event)
{
wxBufferedPaintDC dc(m_targetWindow, wxBUFFER_VIRTUAL_AREA);
dc.SetFont(m_font);
// get window position
auto position = GetPosition();
// get current real position
wxRect rect_update = GetUpdateRegion().GetBox();
const auto count = (uint32)std::ceil((float)rect_update.GetHeight() / m_line_height);
position.y = (rect_update.y / m_line_height) * m_line_height;
// paint background
const wxColour window_colour = COLOR_WHITE;
dc.SetBrush(window_colour);
dc.SetPen(window_colour);
dc.DrawRectangle(rect_update);
//// paint selection
//if (!m_selected_text.eof())
//{
// dc.SetBrush(*wxBLUE_BRUSH);
// dc.SetPen(*wxBLUE_PEN);
// dc.DrawRectangle(m_selection);
//}
sint32 start;
CalcUnscrolledPosition(rect_update.x, rect_update.y, nullptr, &start);
start /= m_line_height;
m_scrolled_to_end = (start + count) >= m_element_count;
OnDraw(dc, start, count, position);
this->Update();
}

View file

@ -0,0 +1,79 @@
#pragma once
#include <wx/wx.h>
#include <unordered_map>
#include <sstream>
#define COLOR_BLACK 0xFF000000
#define COLOR_GREY 0xFFA0A0A0
#define COLOR_LIGHT_GREY 0xFFE0E0E0
#define COLOR_WHITE 0xFFFFFFFF
#define COLOR_RED 0xFF0000FF
class TextList : public wxControl, public wxScrollHelper
{
public:
virtual ~TextList();
TextList(wxWindow* parent, wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize, long style = 0);
void RefreshControl(const wxRect* update_region = nullptr);
void RefreshLine(uint32 line);
wxSize DoGetVirtualSize() const override;
void DoSetSize(int x, int y, int width, int height, int sizeFlags) override;
void SetScrollPos(int orient, int pos, bool refresh) override;
void DrawLineBackground(wxDC& dc, uint32 line, const wxColour& colour, uint32 lines = 1) const;
void DrawLineBackground(wxDC& dc, const wxPoint& position, const wxColour& colour, uint32 lines = 1) const;
bool SetElementCount(size_t element_count);
uint32 GetElementCount() const;
bool IsKeyDown(sint32 keycode);
protected:
void WriteText(wxDC& dc, const wxString& text, wxPoint& position) const;
void WriteText(wxDC& dc, const wxString& text, wxPoint& position, const wxColour& color) const;
void NextLine(wxPoint& position, const wxPoint* start_position = nullptr) const;
virtual void OnDraw(wxDC& dc, sint32 start, sint32 count, const wxPoint& start_position) {};
virtual void OnMouseMove(const wxPoint& position, uint32 line) {}
virtual void OnKeyPressed(sint32 key_code, const wxPoint& position) {}
virtual void OnMouseDown();
virtual void OnMouseUp();
virtual void OnMouseDClick(const wxPoint& position, uint32 line) {}
virtual void OnContextMenu(const wxPoint& position, uint32 line) {}
virtual bool OnShowTooltip(const wxPoint& position, uint32 line);
void OnEraseBackground(wxEraseEvent&) {}
sint32 m_line_height;
sint32 m_char_width;
size_t m_elements_visible = 0;
size_t m_element_count = 0;
bool m_scrolled_to_end = true;
std::wstringstream m_selected_text;
bool m_mouse_down = false;
wxRect m_selection;
wxPanel* m_tooltip_window;
private:
void OnPaintEvent(wxPaintEvent& event);
void OnMouseMoveEvent(wxMouseEvent& event);
void OnKeyDownEvent(wxKeyEvent& event);
void OnKeyUpEvent(wxKeyEvent& event);
void OnMouseDownEvent(wxMouseEvent& event);
void OnMouseUpEvent(wxMouseEvent& event);
void OnMouseDClickEvent(wxMouseEvent& event);
void OnContextMenu(wxContextMenuEvent& event);
void OnTooltipTimer(wxTimerEvent& event);
std::unordered_map<sint32, bool> m_key_states;
wxFont m_font;
wxTimer* m_tooltip_timer;
wxPoint m_mouse_position;
};

View file

@ -0,0 +1,779 @@
#include "gui/components/wxDownloadManagerList.h"
#include "gui/helpers/wxHelpers.h"
#include "util/helpers/SystemException.h"
#include "Cafe/TitleList/GameInfo.h"
#include "gui/components/wxGameList.h"
#include "gui/helpers/wxCustomEvents.h"
#include <wx/imaglist.h>
#include <wx/wupdlock.h>
#include <wx/menu.h>
#include <wx/msgdlg.h>
#include <wx/stattext.h>
#include <wx/sizer.h>
#include <wx/timer.h>
#include <wx/panel.h>
#include <functional>
#include "config/ActiveSettings.h"
#include "gui/ChecksumTool.h"
#include "Cemu/Tools/DownloadManager/DownloadManager.h"
#include "Cafe/TitleList/TitleId.h"
#include "gui/MainWindow.h"
wxDEFINE_EVENT(wxEVT_REMOVE_ENTRY, wxCommandEvent);
wxDownloadManagerList::wxDownloadManagerList(wxWindow* parent, wxWindowID id)
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VIRTUAL)
{
AddColumns();
// tooltip TODO: extract class mb wxPanelTooltip
m_tooltip_window = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER);
auto* tooltip_sizer = new wxBoxSizer(wxVERTICAL);
m_tooltip_text = new wxStaticText(m_tooltip_window, wxID_ANY, wxEmptyString);
tooltip_sizer->Add(m_tooltip_text , 0, wxALL, 5);
m_tooltip_window->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK));
m_tooltip_window->SetSizerAndFit(tooltip_sizer);
m_tooltip_window->Hide();
m_tooltip_timer = new wxTimer(this);
Bind(wxEVT_LIST_COL_CLICK, &wxDownloadManagerList::OnColumnClick, this);
Bind(wxEVT_CONTEXT_MENU, &wxDownloadManagerList::OnContextMenu, this);
Bind(wxEVT_LIST_ITEM_SELECTED, &wxDownloadManagerList::OnItemSelected, this);
Bind(wxEVT_TIMER, &wxDownloadManagerList::OnTimer, this);
Bind(wxEVT_REMOVE_ITEM, &wxDownloadManagerList::OnRemoveItem, this);
Bind(wxEVT_REMOVE_ENTRY, &wxDownloadManagerList::OnRemoveEntry, this);
Bind(wxEVT_CLOSE_WINDOW, &wxDownloadManagerList::OnClose, this);
}
boost::optional<const wxDownloadManagerList::TitleEntry&> wxDownloadManagerList::GetSelectedTitleEntry() const
{
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (selection != wxNOT_FOUND)
{
const auto tmp = GetTitleEntry(selection);
if (tmp.has_value())
return tmp.value();
}
return {};
}
boost::optional<wxDownloadManagerList::TitleEntry&> wxDownloadManagerList::GetSelectedTitleEntry()
{
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (selection != wxNOT_FOUND)
{
const auto tmp = GetTitleEntry(selection);
if (tmp.has_value())
return tmp.value();
}
return {};
}
boost::optional<wxDownloadManagerList::TitleEntry&> wxDownloadManagerList::GetTitleEntry(uint64 titleId, uint16 titleVersion)
{
for(const auto& v : m_data)
{
if (v->entry.titleId == titleId && v->entry.version == titleVersion)
return v->entry;
}
return {};
}
void wxDownloadManagerList::AddColumns()
{
wxListItem col0;
col0.SetId(ColumnTitleId);
col0.SetText(_("Title id"));
col0.SetWidth(120);
InsertColumn(ColumnTitleId, col0);
wxListItem col1;
col1.SetId(ColumnName);
col1.SetText(_("Name"));
col1.SetWidth(260);
InsertColumn(ColumnName, col1);
wxListItem col2;
col2.SetId(ColumnVersion);
col2.SetText(_("Version"));
col2.SetWidth(55);
InsertColumn(ColumnVersion, col2);
wxListItem col3;
col3.SetId(ColumnType);
col3.SetText(_("Type"));
col3.SetWidth(60);
InsertColumn(ColumnType, col3);
wxListItem col4;
col4.SetId(ColumnProgress);
col4.SetText(_("Progress"));
col4.SetWidth(wxLIST_AUTOSIZE_USEHEADER);
InsertColumn(ColumnProgress, col4);
wxListItem col5;
col5.SetId(ColumnStatus);
col5.SetText(_("Status"));
col5.SetWidth(240);
InsertColumn(ColumnStatus, col5);
}
wxString wxDownloadManagerList::OnGetItemText(long item, long column) const
{
if (item >= GetItemCount())
return wxEmptyString;
const auto entry = GetTitleEntry(item);
if (entry.has_value())
return GetTitleEntryText(entry.value(), (ItemColumn)column);
return wxEmptyString;
}
wxItemAttr* wxDownloadManagerList::OnGetItemAttr(long item) const
{
const auto entry = GetTitleEntry(item);
if (entry.has_value())
{
auto& entryData = entry.value();
if (entryData.status == TitleDownloadStatus::Downloading ||
entryData.status == TitleDownloadStatus::Verifying ||
entryData.status == TitleDownloadStatus::Installing)
{
const wxColour kActiveColor{ 0xFFE0E0 };
static wxListItemAttr s_error_attr(GetTextColour(), kActiveColor, GetFont());
return &s_error_attr;
}
else if (entryData.status == TitleDownloadStatus::Installed && entryData.isPackage)
{
const wxColour kActiveColor{ 0xE0FFE0 };
static wxListItemAttr s_error_attr(GetTextColour(), kActiveColor, GetFont());
return &s_error_attr;
}
else if (entryData.status == TitleDownloadStatus::Error)
{
const wxColour kActiveColor{ 0xCCCCF2 };
static wxListItemAttr s_error_attr(GetTextColour(), kActiveColor, GetFont());
return &s_error_attr;
}
}
const wxColour kSecondColor{ 0xFDF9F2 };
static wxListItemAttr s_coloured_attr(GetTextColour(), kSecondColor, GetFont());
return item % 2 == 0 ? nullptr : &s_coloured_attr;
}
boost::optional<wxDownloadManagerList::TitleEntry&> wxDownloadManagerList::GetTitleEntry(long item)
{
long counter = 0;
for (const auto& data : m_sorted_data)
{
if (!data.get().visible)
continue;
if (item != counter++)
continue;
return data.get().entry;
}
return {};
}
boost::optional<const wxDownloadManagerList::TitleEntry&> wxDownloadManagerList::GetTitleEntry(long item) const
{
long counter = 0;
for (const auto& data : m_sorted_data)
{
if (!data.get().visible)
continue;
if (item != counter++)
continue;
return data.get().entry;
}
return {};
}
void wxDownloadManagerList::OnClose(wxCloseEvent& event)
{
event.Skip();
// wait until all tasks are complete
if (m_context_worker.valid())
m_context_worker.get();
g_mainFrame->RequestGameListRefresh(); // todo: add games instead of fully refreshing game list if a game is downloaded
}
void wxDownloadManagerList::OnColumnClick(wxListEvent& event)
{
const int column = event.GetColumn();
if (column == m_sort_by_column)
{
m_sort_less = !m_sort_less;
}
else
{
m_sort_by_column = column;
m_sort_less = true;
}
SortEntries();
event.Skip();
}
void wxDownloadManagerList::RemoveItem(long item)
{
const int item_count = GetItemCount();
const ItemData* ref = nullptr;
long counter = 0;
for(auto it = m_sorted_data.begin(); it != m_sorted_data.end(); ++it)
{
if (!it->get().visible)
continue;
if (item != counter++)
continue;
ref = &(it->get());
m_sorted_data.erase(it);
break;
}
// shouldn't happen
if (ref == nullptr)
return;
for(auto it = m_data.begin(); it != m_data.end(); ++it)
{
if (ref != (*it).get())
continue;
m_data.erase(it);
break;
}
SetItemCount(std::max(0, item_count - 1));
RefreshPage();
}
void wxDownloadManagerList::RemoveItem(const TitleEntry& entry)
{
const int item_count = GetItemCount();
const TitleEntry* ref = &entry;
for (auto it = m_sorted_data.begin(); it != m_sorted_data.end(); ++it)
{
if (ref != &it->get().entry)
continue;
m_sorted_data.erase(it);
break;
}
for (auto it = m_data.begin(); it != m_data.end(); ++it)
{
if (ref != &(*it).get()->entry)
continue;
m_data.erase(it);
break;
}
SetItemCount(std::max(0, item_count - 1));
RefreshPage();
}
void wxDownloadManagerList::OnItemSelected(wxListEvent& event)
{
event.Skip();
m_tooltip_timer->Stop();
const auto selection = event.GetIndex();
if (selection == wxNOT_FOUND)
{
m_tooltip_window->Hide();
return;
}
}
enum ContextMenuEntries
{
kContextMenuRetry,
kContextMenuDownload,
kContextMenuPause,
kContextMenuResume,
};
void wxDownloadManagerList::OnContextMenu(wxContextMenuEvent& event)
{
// still doing work
if (m_context_worker.valid() && !future_is_ready(m_context_worker))
return;
wxMenu menu;
menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &wxDownloadManagerList::OnContextMenuSelected, this);
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (selection == wxNOT_FOUND)
return;
const auto entry = GetTitleEntry(selection);
if (!entry.has_value())
return;
if (entry->isPaused)
menu.Append(kContextMenuResume, _("&Resume"));
else if (entry->status == TitleDownloadStatus::Error)
menu.Append(kContextMenuRetry, _("&Retry"));
else if(entry->status == TitleDownloadStatus::Available)
menu.Append(kContextMenuDownload, _("&Download"));
//else if(entry->status == TitleDownloadStatus::Downloading || entry->status == TitleDownloadStatus::Initializing)
// menu.Append(kContextMenuPause, _("&Pause")); buggy!
PopupMenu(&menu);
// TODO: fix tooltip position
}
void wxDownloadManagerList::SetCurrentDownloadMgr(DownloadManager* dlMgr)
{
std::unique_lock<std::mutex> _l(m_dlMgrMutex);
m_dlMgr = dlMgr;
}
bool wxDownloadManagerList::StartDownloadEntry(const TitleEntry& entry)
{
std::unique_lock<std::mutex> _l(m_dlMgrMutex);
if (m_dlMgr)
m_dlMgr->initiateDownload(entry.titleId, entry.version);
return true;
}
bool wxDownloadManagerList::RetryDownloadEntry(const TitleEntry& entry)
{
StartDownloadEntry(entry);
return true;
}
bool wxDownloadManagerList::PauseDownloadEntry(const TitleEntry& entry)
{
std::unique_lock<std::mutex> _l(m_dlMgrMutex);
if (m_dlMgr)
m_dlMgr->pauseDownload(entry.titleId, entry.version);
return true;
}
void wxDownloadManagerList::OnContextMenuSelected(wxCommandEvent& event)
{
// still doing work
if (m_context_worker.valid() && !future_is_ready(m_context_worker))
return;
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (selection == wxNOT_FOUND)
return;
const auto entry = GetTitleEntry(selection);
if (!entry.has_value())
return;
switch (event.GetId())
{
case kContextMenuDownload:
StartDownloadEntry(entry.value());
break;
case kContextMenuRetry:
RetryDownloadEntry(entry.value());
break;
case kContextMenuResume:
StartDownloadEntry(entry.value());
break;
case kContextMenuPause:
PauseDownloadEntry(entry.value());
break;
}
}
void wxDownloadManagerList::OnTimer(wxTimerEvent& event)
{
if(event.GetTimer().GetId() != m_tooltip_timer->GetId())
{
event.Skip();
return;
}
m_tooltip_window->Show();
}
void wxDownloadManagerList::OnRemoveItem(wxCommandEvent& event)
{
RemoveItem(event.GetInt());
}
void wxDownloadManagerList::OnRemoveEntry(wxCommandEvent& event)
{
wxASSERT(event.GetClientData() != nullptr);
RemoveItem(*(TitleEntry*)event.GetClientData());
}
wxString wxDownloadManagerList::GetTitleEntryText(const TitleEntry& entry, ItemColumn column)
{
switch (column)
{
case ColumnTitleId:
return wxStringFormat2("{:08x}-{:08x}", (uint32)(entry.titleId >> 32), (uint32)(entry.titleId & 0xFFFFFFFF));
case ColumnName:
{
return entry.name;
}
case ColumnType:
return wxStringFormat2("{}", entry.type);
case ColumnVersion:
{
// dont show version for base game unless it is not v0
if (entry.type == EntryType::Base && entry.version == 0)
return "";
if (entry.type == EntryType::DLC && entry.version == 0)
return "";
return wxStringFormat2("v{}", entry.version);
}
case ColumnProgress:
{
if (entry.status == TitleDownloadStatus::Downloading)
{
if (entry.progress >= 1000)
return "100%";
return wxStringFormat2("{:.1f}%", (float)entry.progress / 10.0f); // one decimal
}
else if (entry.status == TitleDownloadStatus::Installing || entry.status == TitleDownloadStatus::Checking || entry.status == TitleDownloadStatus::Verifying)
{
return wxStringFormat2("{0}/{1}", entry.progress, entry.progressMax); // number of processed files/content files
}
return "";
}
case ColumnStatus:
{
if (entry.isPaused)
return _("Paused");
else if (entry.status == TitleDownloadStatus::Available)
{
if (entry.progress == 1)
return _("Not installed (Partially downloaded)");
if (entry.progress == 2)
return _("Update available");
return _("Not installed");
}
else if (entry.status == TitleDownloadStatus::Initializing)
return _("Initializing");
else if (entry.status == TitleDownloadStatus::Checking)
return _("Checking");
else if (entry.status == TitleDownloadStatus::Queued)
return _("Queued");
else if (entry.status == TitleDownloadStatus::Downloading)
return _("Downloading");
else if (entry.status == TitleDownloadStatus::Verifying)
return _("Verifying");
else if (entry.status == TitleDownloadStatus::Installing)
return _("Installing");
else if (entry.status == TitleDownloadStatus::Installed)
return _("Installed");
else if (entry.status == TitleDownloadStatus::Error)
{
wxString errorStatusMsg = _("Error:");
errorStatusMsg.append(" ");
errorStatusMsg.append(entry.errorMsg);
return errorStatusMsg;
}
else
return "Unknown status";
}
}
return wxEmptyString;
}
void wxDownloadManagerList::AddOrUpdateTitle(TitleEntryData_t* obj)
{
const auto& data = obj->GetData();
// if already in list only update
auto entry = GetTitleEntry(data.titleId, data.version);
if (entry.has_value())
{
// update item
entry.value() = data;
RefreshPage(); // more efficient way to do this?
return;
}
m_data.emplace_back(std::make_unique<ItemData>(true, data));
m_sorted_data.emplace_back(*m_data[m_data.size() - 1]);
SetItemCount(m_data.size());
// reapply sort
Filter2(m_filterShowTitles, m_filterShowUpdates, m_filterShowInstalled);
SortEntries();
}
void wxDownloadManagerList::UpdateTitleStatusDepr(TitleEntryData_t* obj, const wxString& text)
{
const auto& data = obj->GetData();
const auto entry = GetTitleEntry(data.titleId, data.version);
// check if already added to list
if (!entry.has_value())
return;
// update gamelist text
for(size_t i = 0; i < m_sorted_data.size(); ++i)
{
if (m_sorted_data[i].get().entry == data)
{
SetItem(i, ColumnStatus, text);
return;
}
}
forceLogDebug_printf("cant update title status of %llx", data.titleId);
}
int wxDownloadManagerList::AddImage(const wxImage& image) const
{
return -1; // m_image_list->Add(image.Scale(kListIconWidth, kListIconWidth, wxIMAGE_QUALITY_BICUBIC));
}
// return <
bool wxDownloadManagerList::SortFunc(std::span<int> sortColumnOrder, const Type_t& v1, const Type_t& v2)
{
cemu_assert_debug(sortColumnOrder.size() == 5);
// visible have always priority
if (!v1.get().visible && v2.get().visible)
return false;
else if (v1.get().visible && !v2.get().visible)
return true;
const auto& entry1 = v1.get().entry;
const auto& entry2 = v2.get().entry;
for (size_t i = 0; i < sortColumnOrder.size(); i++)
{
int sortByColumn = sortColumnOrder[i];
if (sortByColumn == ColumnTitleId)
{
// ensure strong ordering -> use type since only one entry should be now (should be changed if every save for every user is displayed spearately?)
if (entry1.titleId != entry2.titleId)
return entry1.titleId < entry2.titleId;
}
else if (sortByColumn == ColumnName)
{
const int tmp = entry1.name.CmpNoCase(entry2.name);
if (tmp != 0)
return tmp < 0;
}
else if (sortByColumn == ColumnType)
{
if (std::underlying_type_t<EntryType>(entry1.type) != std::underlying_type_t<EntryType>(entry2.type))
return std::underlying_type_t<EntryType>(entry1.type) < std::underlying_type_t<EntryType>(entry2.type);
}
else if (sortByColumn == ColumnVersion)
{
if (entry1.version != entry2.version)
return entry1.version < entry2.version;
}
else if (sortByColumn == ColumnProgress)
{
if (entry1.progress != entry2.progress)
return entry1.progress < entry2.progress;
}
else
{
cemu_assert_debug(false);
return (uintptr_t)&entry1 < (uintptr_t)&entry2;
}
}
return false;
}
#include <boost/container/small_vector.hpp>
void wxDownloadManagerList::SortEntries()
{
boost::container::small_vector<int, 12> s_SortColumnOrder{ ColumnName, ColumnType, ColumnVersion, ColumnTitleId, ColumnProgress };
if (m_sort_by_column != -1)
{
// prioritize column by moving it to first position in the column sort order list
s_SortColumnOrder.erase(std::remove(s_SortColumnOrder.begin(), s_SortColumnOrder.end(), m_sort_by_column), s_SortColumnOrder.end());
s_SortColumnOrder.insert(s_SortColumnOrder.begin(), m_sort_by_column);
}
std::sort(m_sorted_data.begin(), m_sorted_data.end(),
[this, &s_SortColumnOrder](const Type_t& v1, const Type_t& v2) -> bool
{
const bool result = SortFunc({ s_SortColumnOrder.data(), s_SortColumnOrder.size() }, v1, v2);
return m_sort_less ? result : !result;
});
RefreshPage();
}
void wxDownloadManagerList::RefreshPage() { RefreshItems(GetTopItem(), GetTopItem() + GetCountPerPage() + 1); }
int wxDownloadManagerList::Filter(const wxString& filter, const wxString& prefix, ItemColumn column)
{
if (prefix.empty())
return -1;
if (!filter.StartsWith(prefix))
return -1;
int counter = 0;
const auto tmp_filter = filter.substr(prefix.size() - 1).Trim(false);
for (auto&& data : m_data)
{
if (GetTitleEntryText(data->entry, column).Upper().Contains(tmp_filter))
{
data->visible = true;
++counter;
}
else
data->visible = false;
}
return counter;
}
void wxDownloadManagerList::Filter(const wxString& filter)
{
if(filter.empty())
{
std::for_each(m_data.begin(), m_data.end(), [](ItemDataPtr& data) { data->visible = true; });
SetItemCount(m_data.size());
RefreshPage();
return;
}
const auto filter_upper = filter.Upper().Trim(false).Trim(true);
int counter = 0;
if (const auto result = Filter(filter_upper, "TITLEID:", ColumnTitleId) != -1)
counter = result;
else if (const auto result = Filter(filter_upper, "NAME:", ColumnName) != -1)
counter = result;
else if (const auto result = Filter(filter_upper, "TYPE:", ColumnType) != -1)
counter = result;
//else if (const auto result = Filter(filter_upper, "REGION:", ColumnRegion) != -1)
// counter = result;
else if (const auto result = Filter(filter_upper, "VERSION:", ColumnVersion) != -1)
counter = result;
else if(filter_upper == "ERROR")
{
for (auto&& data : m_data)
{
bool visible = false;
//if (data->entry.error != TitleError::None)
// visible = true;
data->visible = visible;
if (visible)
++counter;
}
}
else
{
for (auto&& data : m_data)
{
bool visible = false;
if (data->entry.name.Upper().Contains(filter_upper))
visible = true;
else if (GetTitleEntryText(data->entry, ColumnTitleId).Upper().Contains(filter_upper))
visible = true;
else if (GetTitleEntryText(data->entry, ColumnType).Upper().Contains(filter_upper))
visible = true;
data->visible = visible;
if (visible)
++counter;
}
}
SetItemCount(counter);
RefreshPage();
}
void wxDownloadManagerList::Filter2(bool showTitles, bool showUpdates, bool showInstalled)
{
m_filterShowTitles = showTitles;
m_filterShowUpdates = showUpdates;
m_filterShowInstalled = showInstalled;
if (showTitles && showUpdates && showInstalled)
{
std::for_each(m_data.begin(), m_data.end(), [](ItemDataPtr& data) { data->visible = true; });
SetItemCount(m_data.size());
RefreshPage();
return;
}
size_t counter = 0;
for (auto&& data : m_data)
{
bool visible = false;
TitleIdParser tParser(data->entry.titleId);
bool isInstalled = data->entry.status == TitleDownloadStatus::Installed;
if (tParser.IsBaseTitleUpdate())
{
if (showUpdates && (showInstalled || !isInstalled))
visible = true;
}
else
{
if (showTitles && (showInstalled || !isInstalled))
visible = true;
}
data->visible = visible;
if (visible)
++counter;
}
SetItemCount(counter);
RefreshPage();
}
size_t wxDownloadManagerList::GetCountByType(EntryType type) const
{
size_t result = 0;
for(const auto& data : m_data)
{
if (data->entry.type == type)
++result;
}
return result;
}
void wxDownloadManagerList::ClearItems()
{
m_sorted_data.clear();
m_data.clear();
SetItemCount(0);
RefreshPage();
}
void wxDownloadManagerList::AutosizeColumns()
{
wxAutosizeColumns(this, ColumnTitleId, ColumnMAX - 1);
}

View file

@ -0,0 +1,174 @@
#pragma once
#include "gui/helpers/wxCustomData.h"
#include "config/CemuConfig.h"
#include <wx/listctrl.h>
#include <boost/optional.hpp> // std::optional doesn't support optional reference inner types yet
#include <utility>
#include <vector>
class wxDownloadManagerList : public wxListCtrl
{
friend class TitleManager;
public:
wxDownloadManagerList(wxWindow* parent, wxWindowID id = wxID_ANY);
enum ItemColumn
{
ColumnTitleId = 0,
ColumnName,
ColumnVersion,
ColumnType,
ColumnProgress,
ColumnStatus,
ColumnMAX,
};
enum class EntryType
{
Base,
Update,
DLC,
};
enum class TitleDownloadStatus
{
None,
Available, // available for download
Error, // error state
Queued, // queued for download
Initializing, // downloading/parsing TMD
Checking, // checking for previously downloaded files
Downloading,
Verifying, // verifying downloaded files
Installing,
Installed,
// error state?
};
void SortEntries();
void RefreshPage();
void Filter(const wxString& filter);
void Filter2(bool showTitles, bool showUpdates, bool showInstalled);
[[nodiscard]] size_t GetCountByType(EntryType type) const;
void ClearItems();
void AutosizeColumns();
struct TitleEntry
{
TitleEntry(const EntryType& type, bool isPackage, uint64 titleId, uint16 version, bool isPaused)
: type(type), isPackage(isPackage), titleId(titleId), version(version), isPaused(isPaused) {}
EntryType type;
bool isPaused{};
int icon = -1;
bool isPackage;
uint64 titleId;
wxString name;
uint32_t version{ 0 };
uint32 progress; // downloading: in 1/10th of a percent, installing: number of files
uint32 progressMax{ 0 };
CafeConsoleRegion region;
TitleDownloadStatus status = TitleDownloadStatus::None;
std::string errorMsg;
bool operator==(const TitleEntry& e) const
{
return type == e.type && titleId == e.titleId && version == e.version;
}
bool operator!=(const TitleEntry& e) const { return !(*this == e); }
};
boost::optional<const TitleEntry&> GetSelectedTitleEntry() const;
private:
void AddColumns();
int Filter(const wxString& filter, const wxString& prefix, ItemColumn column);
boost::optional<TitleEntry&> GetSelectedTitleEntry();
boost::optional<TitleEntry&> GetTitleEntry(uint64 titleId, uint16 titleVersion);
class wxPanel* m_tooltip_window;
class wxStaticText* m_tooltip_text;
class wxTimer* m_tooltip_timer;
void OnClose(wxCloseEvent& event);
void OnColumnClick(wxListEvent& event);
void OnContextMenu(wxContextMenuEvent& event);
void OnItemSelected(wxListEvent& event);
void OnContextMenuSelected(wxCommandEvent& event);
void OnTimer(class wxTimerEvent& event);
void OnRemoveItem(wxCommandEvent& event);
void OnRemoveEntry(wxCommandEvent& event);
using TitleEntryData_t = wxCustomData<TitleEntry>;
void AddOrUpdateTitle(TitleEntryData_t* obj);
void UpdateTitleStatusDepr(TitleEntryData_t* obj, const wxString& text);
int AddImage(const wxImage& image) const;
wxString OnGetItemText(long item, long column) const override;
wxItemAttr* OnGetItemAttr(long item) const override;
[[nodiscard]] boost::optional<const TitleEntry&> GetTitleEntry(long item) const;
[[nodiscard]] boost::optional<TitleEntry&> GetTitleEntry(long item);
//[[nodiscard]] boost::optional<const TitleEntry&> GetTitleEntry(const fs::path& path) const;
//[[nodiscard]] boost::optional<TitleEntry&> GetTitleEntry(const fs::path& path);
void SetCurrentDownloadMgr(class DownloadManager* dlMgr);
//bool FixEntry(TitleEntry& entry);
//bool VerifyEntryFiles(TitleEntry& entry);
bool StartDownloadEntry(const TitleEntry& entry);
bool RetryDownloadEntry(const TitleEntry& entry);
bool PauseDownloadEntry(const TitleEntry& entry);
void RemoveItem(long item);
void RemoveItem(const TitleEntry& entry);
struct ItemData
{
ItemData(bool visible, const TitleEntry& entry)
: visible(visible), entry(entry) {}
bool visible;
TitleEntry entry;
};
using ItemDataPtr = std::unique_ptr<ItemData>;
std::vector<ItemDataPtr> m_data;
std::vector<std::reference_wrapper<ItemData>> m_sorted_data;
int m_sort_by_column = ItemColumn::ColumnName;
bool m_sort_less = true;
bool m_filterShowTitles = true;
bool m_filterShowUpdates = true;
bool m_filterShowInstalled = true;
DownloadManager* m_dlMgr{};
std::mutex m_dlMgrMutex;
using Type_t = std::reference_wrapper<const ItemData>;
bool SortFunc(std::span<int> sortColumnOrder, const Type_t& v1, const Type_t& v2);
static wxString GetTitleEntryText(const TitleEntry& entry, ItemColumn column);
std::future<bool> m_context_worker;
};
template <>
struct fmt::formatter<wxDownloadManagerList::EntryType> : formatter<string_view>
{
using base = fmt::formatter<fmt::string_view>;
template <typename FormatContext>
auto format(const wxDownloadManagerList::EntryType& type, FormatContext& ctx)
{
switch (type)
{
case wxDownloadManagerList::EntryType::Base:
return base::format("base", ctx);
case wxDownloadManagerList::EntryType::Update:
return base::format("update", ctx);
case wxDownloadManagerList::EntryType::DLC:
return base::format("DLC", ctx);
}
return base::format(std::to_string(static_cast<std::underlying_type_t<wxDownloadManagerList::EntryType>>(type)), ctx);
}
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,148 @@
#pragma once
#include "config/CemuConfig.h"
#include "Cafe/TitleList/TitleId.h"
#include <future>
#include <mutex>
#include <optional>
#include <wx/listctrl.h>
#include <wx/timer.h>
#include <wx/panel.h>
#include "util/helpers/Semaphore.h"
class wxTitleIdEvent : public wxCommandEvent
{
public:
wxTitleIdEvent(int type, uint64 title_id)
: wxCommandEvent(type), m_title_id(title_id) {}
[[nodiscard]] uint64 GetTitleId() const { return m_title_id; }
private:
uint64 m_title_id;
};
wxDECLARE_EVENT(wxEVT_OPEN_SETTINGS, wxCommandEvent);
wxDECLARE_EVENT(wxEVT_OPEN_GRAPHIC_PACK, wxTitleIdEvent);
wxDECLARE_EVENT(wxEVT_GAMELIST_BEGIN_UPDATE, wxCommandEvent);
wxDECLARE_EVENT(wxEVT_GAMELIST_END_UPDATE, wxCommandEvent);
class wxGameList : public wxListCtrl
{
friend class MainWindow;
public:
enum class Style
{
kList,
kIcons,
kSmallIcons
};
wxGameList(wxWindow* parent, wxWindowID id = wxID_ANY);
~wxGameList();
void SetStyle(Style style, bool save = true);
void LoadConfig();
void SaveConfig(bool flush = false);
bool IsVisible(long item) const; // only available in wxwidgets 3.1.3
void ReloadGameEntries(bool cached = false);
long FindListItemByTitleId(uint64 title_id) const;
void OnClose(wxCloseEvent& event);
private:
std::atomic_bool m_exit = false;
Style m_style;
long GetStyleFlags(Style style) const;
inline static const wxColour kUpdateColor{ 0x3939c3 };
inline static const wxColour kFavoriteColor{ 0xD3F6FD };
inline static const wxColour kSecondColor{ 0xFDF9F2 };
void UpdateItemColors(sint32 startIndex = 0);
enum ItemColumns
{
ColumnHiddenName = 0,
ColumnIcon,
ColumnName,
ColumnVersion,
ColumnDLC,
ColumnGameTime,
ColumnGameStarted,
ColumnRegion,
ColumnFavorite
};
int s_last_column = ColumnName;
int s_direction = 1;
void SortEntries(int column = -1);
struct SortData
{
wxGameList* thisptr;
int column;
int dir;
};
int FindInsertPosition(TitleId titleId);
int SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData);
static int SortFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData);
wxTimer* m_tooltip_timer;
wxToolTip* m_tool_tip;
wxPanel* m_tooltip_window;
wxPoint m_mouse_position;
std::mutex m_async_worker_mutex;
std::thread m_async_worker_thread;
CounterSemaphore m_async_task_count;
std::atomic_bool m_async_worker_active{false};
std::vector<TitleId> m_icon_load_queue;
uint64 m_callbackIdTitleList;
std::string GetNameByTitleId(uint64 titleId);
void HandleTitleListCallback(struct CafeTitleListCallbackEvent* evt);
void AsyncWorkerThread();
void RequestLoadIconAsync(TitleId titleId);
bool QueryIconForTitle(TitleId titleId, int& icon, int& iconSmall);
inline static constexpr int kListIconWidth = 64;
inline static constexpr int kIconWidth = 128;
wxImageList* m_image_list, *m_image_list_small;
std::mutex m_icon_cache_mtx;
std::set<TitleId> m_icon_loaded;
std::map<TitleId, std::pair<int, int>> m_icon_cache; // pair contains icon and small icon
std::map<TitleId, std::string> m_name_cache;
// list mode
void CreateListColumns();
void OnColumnClick(wxListEvent& event);
void OnColumnRightClick(wxListEvent& event);
void ApplyGameListColumnWidths();
void OnColumnBeginResize(wxListEvent& event);
void OnColumnResize(wxListEvent& event);
void OnColumnDrag(wxListEvent& event);
// generic events
void OnKeyDown(wxListEvent& event);
void OnContextMenu(wxContextMenuEvent& event);
void OnContextMenuSelected(wxCommandEvent& event);
void OnGameEntryUpdatedByTitleId(wxTitleIdEvent& event);
void OnItemActivated(wxListEvent& event);
void OnTimer(wxTimerEvent& event);
void OnMouseMove(wxMouseEvent& event);
void OnLeaveWindow(wxMouseEvent& event);
static inline std::once_flag s_launch_file_once;
};

View file

@ -0,0 +1,118 @@
#include "gui/components/wxInputDraw.h"
#include <wx/dcbuffer.h>
wxInputDraw::wxInputDraw(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size)
: wxWindow(parent, id, pos, size, 0, wxPanelNameStr)
{
SetBackgroundStyle(wxBG_STYLE_PAINT);
Bind(wxEVT_PAINT, &wxInputDraw::OnPaintEvent, this);
}
wxInputDraw::~wxInputDraw() { Unbind(wxEVT_PAINT, &wxInputDraw::OnPaintEvent, this); }
void wxInputDraw::OnPaintEvent(wxPaintEvent& event)
{
wxAutoBufferedPaintDC dc(this);
OnRender(dc);
}
void wxInputDraw::OnRender(wxDC& dc)
{
dc.Clear();
glm::vec2 position;
const wxPen *black, *red, *grey;
const wxBrush *black_brush, *red_brush, *grey_brush;
if(IsEnabled())
{
position = m_position;
black = wxBLACK_PEN;
red = wxRED_PEN;
grey = wxGREY_PEN;
black_brush = wxBLACK_BRUSH;
red_brush = wxRED_BRUSH;
grey_brush = wxGREY_BRUSH;
}
else
{
position = {};
black = red = wxMEDIUM_GREY_PEN;
grey = wxLIGHT_GREY_PEN;
black_brush = red_brush = wxMEDIUM_GREY_BRUSH;
grey_brush = wxLIGHT_GREY_BRUSH;
}
dc.SetBackgroundMode(wxSOLID);
dc.SetBackground(*wxWHITE_BRUSH);
const auto size = GetSize();
const auto min_size = (float)std::min(size.GetWidth(), size.GetHeight()) - 1.0f;
const Vector2f middle{min_size / 2.0f, min_size / 2.0f};
// border
const wxRect border{0, 0, (int)min_size, (int)min_size};
dc.SetPen(*black);
dc.DrawRectangle(border);
dc.SetPen(IsEnabled() ? wxPen(wxColour(0x336600)) : *grey); // dark green
dc.DrawCircle((int)middle.x, (int)middle.y, (int)middle.x);
if (m_deadzone > 0)
{
dc.SetPen(*grey);
dc.SetBrush(*wxLIGHT_GREY_BRUSH);
const auto deadzone_size = m_deadzone * min_size / 2.0f;
dc.DrawCircle(
static_cast<int>(middle.x),
static_cast<int>(middle.x),
(int)deadzone_size);
if (length(position) >= m_deadzone)
{
dc.SetPen(*red);
dc.SetBrush(*red_brush);
if (std::abs(1.0f - length(position)) < 0.05f)
{
dc.SetPen(wxPen(wxColour(0x336600)));
dc.SetBrush(wxColour(0x336600));
}
}
else
{
dc.SetPen(*black);
dc.SetBrush(*black_brush);
}
}
else
{
dc.SetPen(*red);
dc.SetBrush(*red_brush);
}
// draw axis
const wxPoint pos
{
static_cast<int>(middle.x + position.x * middle.x),
static_cast<int>(middle.y - position.y * middle.y)
};
dc.DrawCircle(pos.x, pos.y, 2);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
}
void wxInputDraw::SetAxisValue(const glm::vec2& position)
{
m_position = position;
Refresh();
}
void wxInputDraw::SetDeadzone(float deadzone)
{
m_deadzone = deadzone;
Refresh();
}

View file

@ -0,0 +1,24 @@
#pragma once
#include "input/api/ControllerState.h"
#include "util/math/vector2.h"
#include <wx/window.h>
class wxInputDraw : public wxWindow
{
public:
wxInputDraw(wxWindow *parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize);
~wxInputDraw();
virtual void OnRender(wxDC& dc);
void SetAxisValue(const glm::vec2& position);
void SetDeadzone(float deadzone);
private:
void OnPaintEvent(wxPaintEvent& event);
glm::vec2 m_position{};
float m_deadzone{0};
};

View file

@ -0,0 +1,160 @@
#include "gui/components/wxLogCtrl.h"
#include <boost/algorithm/string.hpp>
wxDEFINE_EVENT(EVT_ON_LIST_UPDATED, wxEvent);
wxLogCtrl::wxLogCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
: TextList(parent, id, pos, size, style)
{
m_timer = new wxTimer(this);
this->Bind(wxEVT_TIMER, &wxLogCtrl::OnTimer, this);
this->Bind(EVT_ON_LIST_UPDATED, &wxLogCtrl::OnActiveListUpdated, this);
m_timer->Start(250);
}
wxLogCtrl::~wxLogCtrl()
{
this->Unbind(wxEVT_TIMER, &wxLogCtrl::OnTimer, this);
this->Unbind(EVT_ON_LIST_UPDATED, &wxLogCtrl::OnActiveListUpdated, this);
if(m_timer)
m_timer->Stop();
if(m_update_worker.joinable())
m_update_worker.join();
}
void wxLogCtrl::SetActiveFilter(const std::string& active_filter)
{
if(m_active_filter == active_filter)
return;
m_active_filter = active_filter;
if(m_update_worker.joinable())
m_update_worker.join();
m_update_worker = std::thread(&wxLogCtrl::UpdateActiveEntries, this);
}
const std::string& wxLogCtrl::GetActiveFilter() const
{
return m_active_filter;
}
void wxLogCtrl::SetFilterMessage(bool state)
{
if(m_filter_messages == state)
return;
m_filter_messages = state;
if(m_update_worker.joinable())
m_update_worker.join();
m_update_worker = std::thread(&wxLogCtrl::UpdateActiveEntries, this);
}
bool wxLogCtrl::GetFilterMessage() const
{
return m_filter_messages;
}
void wxLogCtrl::PushEntry(const wxString& filter, const wxString& message)
{
std::unique_lock lock(m_mutex);
m_log_entries.emplace_back(filter, message);
ListIt_t it = m_log_entries.back();
lock.unlock();
if(m_active_filter.empty() || filter == m_active_filter || (m_filter_messages && boost::icontains(message, m_active_filter)))
{
std::unique_lock active_lock(m_active_mutex);
m_active_entries.emplace_back(std::cref(it));
const auto entry_count = m_active_entries.size();
active_lock.unlock();
if(entry_count <= m_elements_visible)
{
RefreshLine(entry_count - 1);
}
}
}
void wxLogCtrl::OnDraw(wxDC& dc, sint32 start, sint32 count, const wxPoint& start_position)
{
wxPoint position = start_position;
std::scoped_lock lock(m_active_mutex);
auto it = std::next(m_active_entries.cbegin(), start);
if(it == m_active_entries.cend())
return;
for (sint32 i = 0; i <= count && it != m_active_entries.cend(); ++i, ++it)
{
wxColour background_colour;
if((start + i) % 2 == 0)
background_colour = COLOR_WHITE;
else
background_colour = 0xFFFDF9F2;
DrawLineBackground(dc, position, background_colour);
dc.SetTextForeground(COLOR_BLACK);
dc.DrawText(it->get().second, position);
NextLine(position, &start_position);
}
}
void wxLogCtrl::OnTimer(wxTimerEvent& event)
{
std::unique_lock lock(m_active_mutex);
const auto count = m_active_entries.size();
if(count == m_element_count)
return;
lock.unlock();
SetElementCount(count);
if(m_scrolled_to_end)
{
Scroll(0, count);
RefreshControl();
}
}
void wxLogCtrl::OnActiveListUpdated(wxEvent& event)
{
std::unique_lock lock(m_active_mutex);
const auto count = m_active_entries.size();
lock.unlock();
SetElementCount(count);
RefreshControl();
}
void wxLogCtrl::UpdateActiveEntries()
{
{
std::scoped_lock lock(m_mutex, m_active_mutex);
m_active_entries.clear();
if(m_active_filter.empty())
{
for (const auto& it : m_log_entries)
m_active_entries.emplace_back(it);
}
else
{
for (const auto& it : m_log_entries)
{
if(it.first == m_active_filter || (m_filter_messages && boost::icontains(it.second, m_active_filter)) )
m_active_entries.emplace_back(it);
}
}
}
wxQueueEvent(this, new wxCommandEvent(EVT_ON_LIST_UPDATED));
}

View file

@ -0,0 +1,36 @@
#pragma once
#include "TextList.h"
class wxLogCtrl : public TextList
{
public:
wxLogCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style);
~wxLogCtrl();
void SetActiveFilter(const std::string& active_filter);
[[nodiscard]] const std::string& GetActiveFilter() const;
void SetFilterMessage(bool state);
[[nodiscard]] bool GetFilterMessage() const;
void PushEntry(const wxString& filter, const wxString& message);
protected:
void OnDraw(wxDC& dc, sint32 start, sint32 count, const wxPoint& start_position);
private:
void OnTimer(wxTimerEvent& event);
void OnActiveListUpdated(wxEvent& event);
wxTimer* m_timer;
std::string m_active_filter;
std::thread m_update_worker;
bool m_filter_messages = false;
std::mutex m_mutex, m_active_mutex;
using ListEntry_t = std::pair<wxString, wxString>;
using ListIt_t = std::list<ListEntry_t>::const_reference;
std::list<ListEntry_t> m_log_entries;
std::list<std::reference_wrapper<const ListEntry_t>> m_active_entries;
void UpdateActiveEntries();
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,162 @@
#pragma once
#include "gui/helpers/wxCustomData.h"
#include "config/CemuConfig.h"
#include <wx/listctrl.h>
#include <boost/optional.hpp> // std::optional doesn't support optional reference inner types yet
#include <utility>
#include <vector>
class wxTitleManagerList : public wxListCtrl
{
friend class TitleManager;
public:
wxTitleManagerList(wxWindow* parent, wxWindowID id = wxID_ANY);
~wxTitleManagerList();
enum ItemColumn
{
ColumnTitleId = 0,
ColumnName,
ColumnType,
ColumnVersion,
ColumnRegion,
ColumnFormat,
ColumnMAX,
};
enum class EntryType
{
Base,
Update,
Dlc,
Save,
System,
};
enum class EntryFormat
{
Folder,
WUD,
WUA,
};
// sort by column, if -1 will sort by last column or default (=titleid)
void SortEntries(int column);
void RefreshPage();
void Filter(const wxString& filter);
[[nodiscard]] size_t GetCountByType(EntryType type) const;
void ClearItems();
void AutosizeColumns();
struct TitleEntry
{
TitleEntry(const EntryType& type, const EntryFormat& format, fs::path path)
: type(type), format(format), path(std::move(path)) {}
EntryType type;
EntryFormat format;
fs::path path;
int icon = -1;
uint64 location_uid;
uint64 title_id;
wxString name;
uint32_t version = 0;
CafeConsoleRegion region;
std::vector<uint32> persistent_ids; // only used for save
};
boost::optional<const TitleEntry&> GetSelectedTitleEntry() const;
private:
void AddColumns();
int Filter(const wxString& filter, const wxString& prefix, ItemColumn column);
boost::optional<TitleEntry&> GetSelectedTitleEntry();
boost::optional<TitleEntry&> GetTitleEntryByUID(uint64 uid);
class wxPanel* m_tooltip_window;
class wxStaticText* m_tooltip_text;
class wxTimer* m_tooltip_timer;
void OnClose(wxCloseEvent& event);
void OnColumnClick(wxListEvent& event);
void OnContextMenu(wxContextMenuEvent& event);
void OnItemSelected(wxListEvent& event);
void OnContextMenuSelected(wxCommandEvent& event);
void OnTimer(class wxTimerEvent& event);
void OnRemoveItem(wxCommandEvent& event);
void OnRemoveEntry(wxCommandEvent& event);
void OnTitleDiscovered(wxCommandEvent& event);
void OnTitleRemoved(wxCommandEvent& event);
void HandleTitleListCallback(struct CafeTitleListCallbackEvent* evt);
void HandleSaveListCallback(struct CafeSaveListCallbackEvent* evt);
using TitleEntryData_t = wxCustomData<TitleEntry>;
void AddTitle(TitleEntryData_t* obj);
int AddImage(const wxImage& image) const;
wxString OnGetItemText(long item, long column) const override;
wxItemAttr* OnGetItemAttr(long item) const override;
[[nodiscard]] boost::optional<const TitleEntry&> GetTitleEntry(long item) const;
[[nodiscard]] boost::optional<TitleEntry&> GetTitleEntry(long item);
[[nodiscard]] boost::optional<const TitleEntry&> GetTitleEntry(const fs::path& path) const;
[[nodiscard]] boost::optional<TitleEntry&> GetTitleEntry(const fs::path& path);
bool VerifyEntryFiles(TitleEntry& entry);
void OnConvertToCompressedFormat(uint64 titleId);
bool DeleteEntry(long index, const TitleEntry& entry);
void RemoveItem(long item);
void RemoveItem(const TitleEntry& entry);
struct ItemData
{
ItemData(bool visible, const TitleEntry& entry)
: visible(visible), entry(entry) {}
bool visible;
TitleEntry entry;
};
using ItemDataPtr = std::unique_ptr<ItemData>;
std::vector<ItemDataPtr> m_data;
std::vector<std::reference_wrapper<ItemData>> m_sorted_data;
int m_last_column_sorted = -1;
bool m_sort_less = true;
using Type_t = std::reference_wrapper<const ItemData>;
bool SortFunc(int column, const Type_t& v1, const Type_t& v2);
static wxString GetTitleEntryText(const TitleEntry& entry, ItemColumn column);
std::future<bool> m_context_worker;
uint64 m_callbackIdTitleList;
uint64 m_callbackIdSaveList;
};
template <>
struct fmt::formatter<wxTitleManagerList::EntryType> : formatter<string_view>
{
using base = fmt::formatter<fmt::string_view>;
template <typename FormatContext>
auto format(const wxTitleManagerList::EntryType& type, FormatContext& ctx)
{
switch (type)
{
case wxTitleManagerList::EntryType::Base:
return base::format("base", ctx);
case wxTitleManagerList::EntryType::Update:
return base::format("update", ctx);
case wxTitleManagerList::EntryType::Dlc:
return base::format("DLC", ctx);
case wxTitleManagerList::EntryType::Save:
return base::format("save", ctx);
case wxTitleManagerList::EntryType::System:
return base::format("system", ctx);
}
return base::format(std::to_string(static_cast<std::underlying_type_t<wxTitleManagerList::EntryType>>(type)), ctx);
}
};