mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-01-01 16:37:10 +00:00
UI: Improvements and fixes for Windows dark mode (#1728)
This commit is contained in:
parent
1382ee0381
commit
934cc3eb9d
17 changed files with 180 additions and 203 deletions
|
|
@ -590,16 +590,16 @@ void CemuUpdateWindow::OnClose(wxCloseEvent& event)
|
|||
if (m_restartRequired && !m_restartFile.empty() && fs::exists(m_restartFile))
|
||||
{
|
||||
PROCESS_INFORMATION pi{};
|
||||
STARTUPINFO si{};
|
||||
STARTUPINFOW si{};
|
||||
si.cb = sizeof(si);
|
||||
|
||||
std::wstring cmdline = GetCommandLineW();
|
||||
const auto index = cmdline.find('"', 1);
|
||||
cemu_assert_debug(index != std::wstring::npos);
|
||||
cmdline = L"\"" + m_restartFile.wstring() + L"\"" + cmdline.substr(index + 1);
|
||||
cmdline = L"\"" + boost::nowide::widen(_pathToUtf8(m_restartFile)) + L"\"" + cmdline.substr(index + 1);
|
||||
|
||||
HANDLE lock = CreateMutex(nullptr, TRUE, L"Global\\cemu_update_lock");
|
||||
CreateProcess(nullptr, (wchar_t*)cmdline.c_str(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);
|
||||
HANDLE lock = CreateMutexW(nullptr, TRUE, L"Global\\cemu_update_lock");
|
||||
CreateProcessW(nullptr, (wchar_t*)cmdline.c_str(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,17 +141,15 @@ wxGameList::wxGameList(wxWindow* parent, wxWindowID id)
|
|||
const auto& config = GetWxGUIConfig();
|
||||
|
||||
char transparent_bitmap[kIconWidth * kIconWidth * 4] = {};
|
||||
memset((void*)transparent_bitmap, wxSystemSettings::GetAppearance().IsDark() ? 0xFF : 0x00, sizeof(transparent_bitmap));
|
||||
memset(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.Add(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.Add(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:
|
||||
|
|
@ -458,25 +452,22 @@ void wxGameList::UpdateItemColors(sint32 startIndex)
|
|||
{
|
||||
wxWindowUpdateLocker lock(this);
|
||||
|
||||
wxColour bgColourPrimary = GetBackgroundColour();
|
||||
wxColour bgColourSecondary = wxHelper::CalculateAccentColour(bgColourPrimary);
|
||||
|
||||
for (int i = startIndex; i < GetItemCount(); ++i)
|
||||
{
|
||||
const auto titleId = (uint64)GetItemData(i);
|
||||
const uint64 titleId = GetItemData(i);
|
||||
if (GetConfig().IsGameListFavorite(titleId))
|
||||
{
|
||||
SetItemBackgroundColour(i, kFavoriteColor);
|
||||
SetItemTextColour(i, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||
}
|
||||
else if ((i&1) != 0)
|
||||
else if ((i % 2) != 0)
|
||||
{
|
||||
SetItemBackgroundColour(i, bgColourPrimary);
|
||||
SetItemBackgroundColour(i, kPrimaryColor);
|
||||
SetItemTextColour(i, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetItemBackgroundColour(i, bgColourSecondary);
|
||||
SetItemBackgroundColour(i, kAlternateColor);
|
||||
SetItemTextColour(i, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||
}
|
||||
}
|
||||
|
|
@ -484,7 +475,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 +487,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 +533,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 +557,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 +828,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 +990,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 +1012,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 +1105,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 +1150,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 +1320,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 +1365,7 @@ bool wxGameList::QueryIconForTitle(TitleId titleId, int& icon, int& iconSmall)
|
|||
return true;
|
||||
}
|
||||
|
||||
void wxGameList::DeleteCachedStrings()
|
||||
void wxGameList::DeleteCachedStrings()
|
||||
{
|
||||
m_name_cache.clear();
|
||||
}
|
||||
|
|
@ -1415,9 +1390,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 +1408,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 +1483,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 +1501,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 +1610,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());
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@
|
|||
#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 <wx/settings.h>
|
||||
#include <Cafe/TitleList/GameInfo.h>
|
||||
|
||||
#include "wxHelper.h"
|
||||
#include "util/helpers/Semaphore.h"
|
||||
|
||||
class wxTitleIdEvent : public wxCommandEvent
|
||||
|
|
@ -66,7 +66,8 @@ private:
|
|||
|
||||
const wxColour kUpdateColor{ wxSystemSettings::SelectLightDark(wxColour(195, 57, 57), wxColour(84, 29, 29)) };
|
||||
const wxColour kFavoriteColor{ wxSystemSettings::SelectLightDark(wxColour(253, 246, 211), wxColour(82, 84, 48)) };
|
||||
const wxColour kSecondColor{ wxSystemSettings::SelectLightDark(wxColour(242, 249, 253), wxColour(34, 34, 34)) };
|
||||
const wxColour kPrimaryColor = GetBackgroundColour();
|
||||
const wxColour kAlternateColor = wxHelper::CalculateAccentColour(kPrimaryColor);
|
||||
void UpdateItemColors(sint32 startIndex = 0);
|
||||
|
||||
enum ItemColumns : int
|
||||
|
|
@ -89,7 +90,7 @@ private:
|
|||
{
|
||||
wxGameList* thisptr;
|
||||
ItemColumns column;
|
||||
bool asc;
|
||||
int dir;
|
||||
};
|
||||
|
||||
int FindInsertPosition(TitleId titleId);
|
||||
|
|
@ -109,7 +110,7 @@ private:
|
|||
std::vector<TitleId> m_icon_load_queue;
|
||||
|
||||
uint64 m_callbackIdTitleList;
|
||||
|
||||
|
||||
std::string GetNameByTitleId(uint64 titleId);
|
||||
|
||||
void HandleTitleListCallback(struct CafeTitleListCallbackEvent* evt);
|
||||
|
|
@ -122,7 +123,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<TitleId> m_icon_loaded;
|
||||
|
|
|
|||
|
|
@ -25,10 +25,9 @@ namespace wxHelper
|
|||
|
||||
inline wxColour CalculateAccentColour(const wxColour& bgColour)
|
||||
{
|
||||
wxColour bgColourSecondary;
|
||||
const uint32 bgLightness = (bgColour.GetRed() + bgColour.GetGreen() + bgColour.GetBlue()) / 3;
|
||||
const bool isDarkTheme = bgLightness < 128;
|
||||
bgColourSecondary = bgColour.ChangeLightness(isDarkTheme ? 110 : 90); // color for even rows
|
||||
wxColour bgColourSecondary = bgColour.ChangeLightness(isDarkTheme ? 110 : 90); // color for even rows
|
||||
// for very light themes we'll use a blue tint to match the older Windows Cemu look
|
||||
if (bgLightness > 250)
|
||||
bgColourSecondary = wxColour(bgColour.Red() - 13, bgColour.Green() - 6, bgColour.Blue() - 2);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue