diff --git a/src/gui/wxgui/components/wxGameList.cpp b/src/gui/wxgui/components/wxGameList.cpp index f760e0b3..e682d671 100644 --- a/src/gui/wxgui/components/wxGameList.cpp +++ b/src/gui/wxgui/components/wxGameList.cpp @@ -144,14 +144,12 @@ wxGameList::wxGameList(wxWindow* parent, wxWindowID id) memset((void*)transparent_bitmap, wxSystemSettings::GetAppearance().IsDark() ? 0xFF : 0x00, sizeof(transparent_bitmap)); wxBitmap blank(transparent_bitmap, kIconWidth, kIconWidth); - m_image_list_data = {}; - m_image_list_data.emplace_back(wxBitmapBundle::FromBitmap(blank)); - wxListCtrl::SetNormalImages(m_image_list_data); + m_image_list_data.Replace(0, blank); + wxListCtrl::SetImageList(&m_image_list_data, wxIMAGE_LIST_NORMAL); - m_image_list_small_data = {}; wxBitmap::Rescale(blank, {kListIconWidth, kListIconWidth}); - m_image_list_small_data.emplace_back(wxBitmapBundle::FromBitmap(blank)); - wxListCtrl::SetSmallImages(m_image_list_small_data); + m_image_list_small_data.Replace(0, blank); + wxListCtrl::SetImageList(&m_image_list_small_data, wxIMAGE_LIST_SMALL); InsertColumn(ColumnHiddenName, "", wxLIST_FORMAT_LEFT, 0); if(config.show_icon_column) @@ -251,13 +249,13 @@ void wxGameList::OnGameListSize(wxSizeEvent &event) for(int i = GetColumnCount() - 1; i > 0; i--) { #ifdef wxHAS_LISTCTRL_COLUMN_ORDER - if(GetColumnWidth(GetColumnIndexFromOrder(i)) > 0) + if(GetColumnWidth(GetColumnIndexFromOrder(i)) > 0) { last_col_index = GetColumnIndexFromOrder(i); break; } #else - if(GetColumnWidth(i) > 0) + if(GetColumnWidth(i) > 0) { last_col_index = i; break; @@ -302,12 +300,6 @@ int wxGameList::GetColumnDefaultWidth(int column) switch (column) { case ColumnIcon: -#if __WXMSW__ - // note: this is another workaround that could be used to fix the icon offset, but instead of this there's a vcpkg patch for wxWidgets that fixes the icon offset - // wxWidgets offsets the icon in the REPORT view so it's cut off if the column width is set to 64px, see https://github.com/wxWidgets/wxWidgets/blob/09f433faf39aab3f25b3c564b82448bb845fae56/src/msw/listctrl.cpp#L3091 - // so add 6px to the column width to compensate, and add another 6px to the right side so that it has equal whitespace on both sides - // return kListIconWidth + 6 + 6; -#endif return kListIconWidth; case ColumnName: return DefaultColumnSize::name; @@ -410,11 +402,13 @@ void wxGameList::SetStyle(Style style, bool save) switch(style) { case Style::kIcons: - wxListCtrl::SetNormalImages(m_image_list_data); + wxListCtrl::SetImageList(&m_image_list_data, wxIMAGE_LIST_NORMAL); break; case Style::kSmallIcons: + wxListCtrl::SetImageList(&m_image_list_small_data, wxIMAGE_LIST_NORMAL); + break; case Style::kList: - wxListCtrl::SetSmallImages(m_image_list_small_data); + wxListCtrl::SetImageList(&m_image_list_small_data, wxIMAGE_LIST_SMALL); break; } @@ -443,7 +437,7 @@ long wxGameList::GetStyleFlags(Style style) const switch (style) { case Style::kList: - return (wxLC_SINGLE_SEL | wxLC_REPORT); + return (wxLC_SINGLE_SEL | wxLC_VRULES | wxLC_REPORT); case Style::kIcons: return (wxLC_SINGLE_SEL | wxLC_ICON); case Style::kSmallIcons: @@ -484,7 +478,7 @@ void wxGameList::UpdateItemColors(sint32 startIndex) static inline int order_to_int(const std::weak_ordering &wo) { - // no easy conversion seems to exists in C++20 + // no easy conversion seems to exist in C++20 if (wo == std::weak_ordering::less) return -1; else if (wo == std::weak_ordering::greater) @@ -496,27 +490,24 @@ std::weak_ordering wxGameList::SortComparator(uint64 titleId1, uint64 titleId2, { auto titleLastPlayed = [](uint64_t id) { - iosu::pdm::GameListStat playTimeStat{}; - iosu::pdm::GetStatForGamelist(id, playTimeStat); - return playTimeStat; + iosu::pdm::GameListStat playTimeStat{}; + iosu::pdm::GetStatForGamelist(id, playTimeStat); + return playTimeStat; }; auto titlePlayMinutes = [](uint64_t id) { - iosu::pdm::GameListStat playTimeStat; - if (!iosu::pdm::GetStatForGamelist(id, playTimeStat)) - return 0u; - return playTimeStat.numMinutesPlayed; + iosu::pdm::GameListStat playTimeStat; + if (!iosu::pdm::GetStatForGamelist(id, playTimeStat)) + return 0u; + return playTimeStat.numMinutesPlayed; }; auto titleRegion = [](uint64_t id) { - return CafeTitleList::GetGameInfo(id).GetRegion(); + return CafeTitleList::GetGameInfo(id).GetRegion(); }; - if (!sortData->asc) - std::swap(titleId1, titleId2); - switch(sortData->column) { default: @@ -545,7 +536,7 @@ std::weak_ordering wxGameList::SortComparator(uint64 titleId1, uint64 titleId2, int wxGameList::SortFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData) { const auto sort_data = (SortData*)sortData; - return order_to_int(sort_data->thisptr->SortComparator((uint64)item1, (uint64)item2, sort_data)); + return sort_data->dir * order_to_int(sort_data->thisptr->SortComparator((uint64)item1, (uint64)item2, sort_data)); } void wxGameList::SortEntries(int column) @@ -569,7 +560,7 @@ void wxGameList::SortEntries(int column) case ColumnRegion: case ColumnTitleID: { - SortData data{this, ItemColumns{column}, ascending}; + SortData data{this, ItemColumns{column}, ascending ? 1 : -1}; SortItems(SortFunction, (wxIntPtr)&data); ShowSortIndicator(column, ascending); break; @@ -840,8 +831,8 @@ void wxGameList::OnContextMenuSelected(wxCommandEvent& event) int icon_small; if (!QueryIconForTitle(title_id, icon_large, icon_small)) break; - auto icon = m_image_list_data[icon_large]; - auto newClipboardData = wxBitmapDataObject(icon.GetBitmap(icon.GetDefaultSize())); + auto icon = m_image_list_data.GetIcon(icon_large); + auto newClipboardData = wxBitmapDataObject(icon); wxClipboard::Get()->SetData(&newClipboardData); wxClipboard::Get()->Close(); } @@ -1002,7 +993,7 @@ void wxGameList::ApplyGameListColumnWidths() const auto& config = GetWxGUIConfig(); wxWindowUpdateLocker lock(this); if(config.show_icon_column) - SetColumnWidth(ColumnIcon, GetColumnDefaultWidth(ColumnIcon)); + SetColumnWidth(ColumnIcon, kListIconWidth); else SetColumnWidth(ColumnIcon, 0); SetColumnWidth(ColumnName, config.column_width.name); @@ -1024,13 +1015,13 @@ void wxGameList::OnColumnBeginResize(wxListEvent& event) for(int i = GetColumnCount() - 1; i > 0; i--) { #ifdef wxHAS_LISTCTRL_COLUMN_ORDER - if(GetColumnWidth(GetColumnIndexFromOrder(i)) > 0) + if(GetColumnWidth(GetColumnIndexFromOrder(i)) > 0) { last_col_index = GetColumnIndexFromOrder(i); break; } #else - if(GetColumnWidth(i) > 0) + if(GetColumnWidth(i) > 0) { last_col_index = i; break; @@ -1117,19 +1108,6 @@ void wxGameList::OnGameEntryUpdatedByTitleId(wxTitleIdEvent& event) TitleId baseTitleId = gameInfo.GetBaseTitleId(); bool isNewEntry = false; - if (m_style == Style::kIcons) - { - wxListCtrl::SetNormalImages(m_image_list_data); - } - else if (m_style == Style::kSmallIcons) - { - wxListCtrl::SetSmallImages(m_image_list_small_data); - } - else if (m_style == Style::kList) - { - wxListCtrl::SetSmallImages(m_image_list_small_data); - } - int icon = -1; /* 0 is the default empty icon */ int icon_small = -1; /* 0 is the default empty icon */ QueryIconForTitle(baseTitleId, icon, icon_small); @@ -1175,7 +1153,7 @@ void wxGameList::OnGameEntryUpdatedByTitleId(wxTitleIdEvent& event) wxString minutesText = formatWxString(wxPLURAL("{} minute", "{} minutes", minutes), minutes); SetItem(index, ColumnGameTime, hoursText + " " + minutesText); } - + // last played if (playTimeStat.last_played.year != 0) { @@ -1345,11 +1323,11 @@ void wxGameList::AsyncWorkerThread() wxMemoryInputStream tmp_stream(tgaData->data(), tgaData->size()); const wxImage image(tmp_stream); // todo - is wxImageList thread safe? - m_image_list_data.emplace_back(image.Scale(kIconWidth, kIconWidth, wxIMAGE_QUALITY_BICUBIC)); - m_image_list_small_data.emplace_back(image.Scale(kListIconWidth, kListIconWidth, wxIMAGE_QUALITY_BICUBIC)); + int icon = m_image_list_data.Add(image.Scale(kIconWidth, kIconWidth, wxIMAGE_QUALITY_BICUBIC)); + int icon_small = m_image_list_small_data.Add(image.Scale(kListIconWidth, kListIconWidth, wxIMAGE_QUALITY_BICUBIC)); // store in cache m_icon_cache_mtx.lock(); - m_icon_cache.try_emplace(titleId, m_image_list_data.size() - 1, m_image_list_small_data.size() - 1); + m_icon_cache.try_emplace(titleId, icon, icon_small); m_icon_cache_mtx.unlock(); iconSuccessfullyLoaded = true; } @@ -1390,7 +1368,7 @@ bool wxGameList::QueryIconForTitle(TitleId titleId, int& icon, int& iconSmall) return true; } -void wxGameList::DeleteCachedStrings() +void wxGameList::DeleteCachedStrings() { m_name_cache.clear(); } @@ -1415,9 +1393,9 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) // Obtain and convert icon [&]() { - int iconIndex, smallIconIndex; + int iconIdx, smallIconIdx; - if (!QueryIconForTitle(titleId, iconIndex, smallIconIndex)) + if (!QueryIconForTitle(titleId, iconIdx, smallIconIdx)) { cemuLog_log(LogType::Force, "Icon hasn't loaded"); return; @@ -1433,8 +1411,8 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) iconPath = outIconDir / fmt::format("{:016x}.png", gameInfo.GetBaseTitleId()); wxFileOutputStream pngFileStream(_pathToUtf8(iconPath.value())); - const auto icon = m_image_list_data[iconIndex]; - wxBitmap bitmap{icon.GetBitmap(wxDefaultSize)}; + const auto icon = m_image_list_data.GetIcon(iconIdx); + wxBitmap bitmap{icon}; wxImage image = bitmap.ConvertToImage(); wxPNGHandler pngHandler; if (!pngHandler.SaveFile(&image, pngFileStream, false)) @@ -1508,9 +1486,9 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) // Obtain and convert icon [&]() { - int iconIndex, smallIconIndex; + int iconIdx, smallIconIdx; - if (!QueryIconForTitle(titleId, iconIndex, smallIconIndex)) + if (!QueryIconForTitle(titleId, iconIdx, smallIconIdx)) { cemuLog_log(LogType::Force, "Icon hasn't loaded"); return; @@ -1526,8 +1504,8 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) iconPath = outIconDir / fmt::format("{:016x}.png", gameInfo.GetBaseTitleId()); wxFileOutputStream pngFileStream(_pathToUtf8(iconPath.value())); - const auto icon = m_image_list_data[iconIndex]; - wxBitmap bitmap{icon.GetBitmap(wxDefaultSize)}; + const auto icon = m_image_list_data.GetIcon(iconIdx); + wxBitmap bitmap{icon}; wxImage image = bitmap.ConvertToImage(); wxPNGHandler pngHandler; if (!pngHandler.SaveFile(&image, pngFileStream, false)) @@ -1635,14 +1613,14 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) cemuLog_log(LogType::Force, "Icon hasn't loaded"); return; } - const auto icon = m_image_list_data[iconIdx]; + const auto icon = m_image_list_data.GetIcon(iconIdx); const auto folder = ActiveSettings::GetUserDataPath("icons"); if (!fs::exists(folder) && !fs::create_directories(folder)) { cemuLog_log(LogType::Force, "Failed to create icon directory"); return; } - wxBitmap bitmap{icon.GetBitmap(wxDefaultSize)}; + wxBitmap bitmap{icon}; icon_path = folder / fmt::format("{:016x}.ico", titleId); auto stream = wxFileOutputStream(icon_path->wstring()); diff --git a/src/gui/wxgui/components/wxGameList.h b/src/gui/wxgui/components/wxGameList.h index e4f90ca3..40edf0e1 100644 --- a/src/gui/wxgui/components/wxGameList.h +++ b/src/gui/wxgui/components/wxGameList.h @@ -89,7 +89,7 @@ private: { wxGameList* thisptr; ItemColumns column; - bool asc; + int dir; }; int FindInsertPosition(TitleId titleId); @@ -109,7 +109,7 @@ private: std::vector m_icon_load_queue; uint64 m_callbackIdTitleList; - + std::string GetNameByTitleId(uint64 titleId); void HandleTitleListCallback(struct CafeTitleListCallbackEvent* evt); @@ -122,7 +122,8 @@ private: inline static constexpr int kListIconWidth = 64; inline static constexpr int kIconWidth = 128; - wxWithImages::Images m_image_list_data, m_image_list_small_data; + wxImageList m_image_list_data = wxImageList(kIconWidth, kIconWidth, false, 1); + wxImageList m_image_list_small_data = wxImageList(kListIconWidth, kListIconWidth, false, 1); std::mutex m_icon_cache_mtx; std::set m_icon_loaded;