diff --git a/bin/GuiConfigs/Classic (Bright).qss b/bin/GuiConfigs/Classic (Bright).qss
index 130a8ab4f6..72c7ea7041 100644
--- a/bin/GuiConfigs/Classic (Bright).qss
+++ b/bin/GuiConfigs/Classic (Bright).qss
@@ -43,20 +43,6 @@ QLabel#gamelist_icon_background_color {
color: rgba(209,209,209,255);
}
-/* game grid stylesheet */
-QTableWidget#game_grid {
- font-weight:600;
- font-size:8pt;
- font-family:Lucida Grande;
- color: rgba(51,51,51,255);
-}
-QTableWidget#game_grid::item:selected:active {
- selection-background-color: #0078D7;
-}
-QTableWidget#game_grid::item:selected:!active {
- selection-background-color: #008cff;
-}
-
/* log stylesheet */
QTextEdit#tty_frame {
background-color:#ffffff;
@@ -131,3 +117,33 @@ QLabel#rsx_debugger_display_buffer {
QLabel#l_controller {
color:#434343;
}
+
+/* Game Grid */
+#game_list_grid_item[selected="true"] {
+ background: #0078D7;
+}
+#game_list_grid_item:hover {
+ background: #008cff;
+}
+#game_list_grid_item:focus {
+ background: #0078D7;
+}
+
+/* Game Grid Font */
+#game_list_grid_item #game_list_grid_item_title_label {
+ font-weight: 600;
+ font-size: 8pt;
+ font-family: Lucida Grande;
+ color: rgba(51,51,51,255);
+}
+
+/* Game Grid hover and focus: we need to handle properties differently when using descendants */
+#game_list_grid_item[selected="true"] #game_list_grid_item_title_label {
+ color: #fff;
+}
+#game_list_grid_item[hover="true"] #game_list_grid_item_title_label {
+ color: #fff;
+}
+#game_list_grid_item[focus="true"] #game_list_grid_item_title_label {
+ color: #fff;
+}
diff --git a/bin/GuiConfigs/Darker Style by TheMitoSan.qss b/bin/GuiConfigs/Darker Style by TheMitoSan.qss
index f46cc2e0b1..c4d867cd1b 100644
--- a/bin/GuiConfigs/Darker Style by TheMitoSan.qss
+++ b/bin/GuiConfigs/Darker Style by TheMitoSan.qss
@@ -197,17 +197,6 @@ QLabel#l_controller {
color: #FFF;
}
-/* Game Grid Font */
-QTableWidget#game_grid {
- font-weight: 600;
- font-size: 8pt;
- font-family: Lucida Grande;
- color: #FFF;
-}
-QTableWidget#game_grid::item:selected:!active {
- selection-background-color: #313f4e;
-}
-
/* Slider on toolbar */
QWidget#sizeSliderContainer {
background: transparent;
@@ -344,24 +333,54 @@ QLabel#debugger_frame_pc {
/* Tree view changes*/
QTreeView::branch:has-children:!has-siblings:closed,
QTreeView::branch:closed:has-children:has-siblings {
- border-image: none;
- image: url("GuiConfigs/list_arrow_white.png");
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_white.png");
}
QTreeView::branch:open:has-children:!has-siblings,
-QTreeView::branch:open:has-children:has-siblings {
- border-image: none;
- image: url("GuiConfigs/list_arrow_down_white.png");
+QTreeView::branch:open:has-children:has-siblings {
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_down_white.png");
}
QTreeView::branch:has-children:!has-siblings:closed:hover,
QTreeView::branch:closed:has-children:has-siblings:hover {
- border-image: none;
- image: url("GuiConfigs/list_arrow_blue.png");
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_blue.png");
}
QTreeView::branch:open:has-children:!has-siblings:hover,
-QTreeView::branch:open:has-children:has-siblings:hover {
- border-image: none;
- image: url("GuiConfigs/list_arrow_down_blue.png");
+QTreeView::branch:open:has-children:has-siblings:hover {
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_down_blue.png");
+}
+
+/* Game Grid */
+#game_list_grid_item[selected="true"] {
+ background: #313f4e;
+}
+#game_list_grid_item:hover {
+ background: #313f4e;
+}
+#game_list_grid_item:focus {
+ background: #0078D7;
+}
+
+/* Game Grid Font */
+#game_list_grid_item #game_list_grid_item_title_label {
+ font-weight: 600;
+ font-size: 8pt;
+ font-family: Lucida Grande;
+ color: #FFF;
+}
+
+/* Game Grid hover and focus: we need to handle properties differently when using descendants */
+#game_list_grid_item[selected="true"] #game_list_grid_item_title_label {
+ background-color: #313f4e;
+}
+#game_list_grid_item[hover="true"] #game_list_grid_item_title_label {
+ background-color: #313f4e;
+}
+#game_list_grid_item[focus="true"] #game_list_grid_item_title_label {
+ background-color: #0078D7;
}
diff --git a/bin/GuiConfigs/Envy.qss b/bin/GuiConfigs/Envy.qss
index 956ec34b1f..8bc80166c1 100644
--- a/bin/GuiConfigs/Envy.qss
+++ b/bin/GuiConfigs/Envy.qss
@@ -454,7 +454,7 @@ QListWidget::item:selected {
}
QListWidget::item:hover {
- background-color: #30333a;
+ background-color: #30333a;
color: #8cf944;
border-radius: 0.25em;
}
@@ -565,7 +565,7 @@ QTableView {
}
QTableView::item:hover {
- color: #8cf944;
+ color: #8cf944;
}
/* Game Icon Background */
@@ -641,24 +641,42 @@ QLabel#debugger_frame_pc {
/* Tree view changes*/
QTreeView::branch:has-children:!has-siblings:closed,
QTreeView::branch:closed:has-children:has-siblings {
- border-image: none;
- image: url("GuiConfigs/list_arrow_white.png");
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_white.png");
}
QTreeView::branch:open:has-children:!has-siblings,
-QTreeView::branch:open:has-children:has-siblings {
- border-image: none;
- image: url("GuiConfigs/list_arrow_down_white.png");
+QTreeView::branch:open:has-children:has-siblings {
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_down_white.png");
}
QTreeView::branch:has-children:!has-siblings:closed:hover,
QTreeView::branch:closed:has-children:has-siblings:hover {
- border-image: none;
- image: url("GuiConfigs/list_arrow_green.png");
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_green.png");
}
QTreeView::branch:open:has-children:!has-siblings:hover,
-QTreeView::branch:open:has-children:has-siblings:hover {
- border-image: none;
- image: url("GuiConfigs/list_arrow_down_green.png");
+QTreeView::branch:open:has-children:has-siblings:hover {
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_down_green.png");
+}
+
+/* Game Grid */
+#game_list_grid_item[selected="true"] {
+ background: #2d3038;
+}
+#game_list_grid_item:focus {
+ background: #2d3038;
+}
+
+/* Game Grid hover and focus: we need to handle properties differently when using descendants */
+#game_list_grid_item[selected="true"] #game_list_grid_item_title_label {
+ background-color: #2d3038;
+ color: #8cf944;
+}
+#game_list_grid_item[focus="true"] #game_list_grid_item_title_label {
+ background-color: #2d3038;
+ color: #8cf944;
}
diff --git a/bin/GuiConfigs/Kuroi (Dark) by Ani.qss b/bin/GuiConfigs/Kuroi (Dark) by Ani.qss
index 0f63d148ed..ba46bb5831 100644
--- a/bin/GuiConfigs/Kuroi (Dark) by Ani.qss
+++ b/bin/GuiConfigs/Kuroi (Dark) by Ani.qss
@@ -249,20 +249,6 @@ QWidget#trophy_notification_frame {
color: #e6e6e6;
}
-/* Game Grid Font */
-QTableWidget#game_grid {
- font-weight: 600;
- font-size: 8pt;
- font-family: Lucida Grande;
- selection-color: #e6e6e6;
-}
-QTableWidget#game_grid::item:selected:active {
- selection-background-color: #4c4b4b;
-}
-QTableWidget#game_grid::item:selected:!active {
- selection-background-color: #3d3d3d;
-}
-
/* Set Toolbar Slider Size */
QSlider#sizeSlider::groove:horizontal {
border: 0em solid transparent;
@@ -347,24 +333,54 @@ QLabel#tty_text {
/* Tree view changes*/
QTreeView::branch:has-children:!has-siblings:closed,
QTreeView::branch:closed:has-children:has-siblings {
- border-image: none;
- image: url("GuiConfigs/list_arrow_white.png");
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_white.png");
}
QTreeView::branch:open:has-children:!has-siblings,
-QTreeView::branch:open:has-children:has-siblings {
- border-image: none;
- image: url("GuiConfigs/list_arrow_down_white.png");
+QTreeView::branch:open:has-children:has-siblings {
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_down_white.png");
}
QTreeView::branch:has-children:!has-siblings:closed:hover,
QTreeView::branch:closed:has-children:has-siblings:hover {
- border-image: none;
- image: url("GuiConfigs/list_arrow_blue.png");
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_blue.png");
}
QTreeView::branch:open:has-children:!has-siblings:hover,
-QTreeView::branch:open:has-children:has-siblings:hover {
- border-image: none;
- image: url("GuiConfigs/list_arrow_down_blue.png");
+QTreeView::branch:open:has-children:has-siblings:hover {
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_down_blue.png");
+}
+
+/* Game Grid */
+#game_list_grid_item[selected="true"] {
+ background: #3d3d3d;
+}
+#game_list_grid_item:hover {
+ background: #3d3d3d;
+}
+#game_list_grid_item:focus {
+ background: #4c4b4b;
+}
+
+/* Game Grid Font */
+#game_list_grid_item #game_list_grid_item_title_label {
+ font-weight: 600;
+ font-size: 8pt;
+ font-family: Lucida Grande;
+}
+
+/* Game Grid hover and focus: we need to handle properties differently when using descendants */
+#game_list_grid_item[selected="true"] #game_list_grid_item_title_label {
+ background-color: #3d3d3d;
+}
+#game_list_grid_item[hover="true"] #game_list_grid_item_title_label {
+ background-color: #3d3d3d;
+}
+#game_list_grid_item[focus="true"] #game_list_grid_item_title_label {
+ color: #e6e6e6;
+ background-color: #4c4b4b;
}
diff --git a/bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss b/bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss
index 2c48ba05c9..44f7902353 100644
--- a/bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss
+++ b/bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss
@@ -194,17 +194,6 @@ QLabel#l_controller {
color: #fff;
}
-/* Game Grid Font */
-QTableWidget#game_grid {
- font-weight: 600;
- font-size: 8pt;
- font-family: Lucida Grande;
- color: #fff;
-}
-QTableWidget#game_grid::item:selected:!active {
- selection-background-color: #354454;
-}
-
/* Slider on toolbar */
QWidget#sizeSliderContainer {
background: transparent;
@@ -341,24 +330,54 @@ QLabel#debugger_frame_pc {
/* Tree view changes*/
QTreeView::branch:has-children:!has-siblings:closed,
QTreeView::branch:closed:has-children:has-siblings {
- border-image: none;
- image: url("GuiConfigs/list_arrow_white.png");
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_white.png");
}
QTreeView::branch:open:has-children:!has-siblings,
-QTreeView::branch:open:has-children:has-siblings {
- border-image: none;
- image: url("GuiConfigs/list_arrow_down_white.png");
+QTreeView::branch:open:has-children:has-siblings {
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_down_white.png");
}
QTreeView::branch:has-children:!has-siblings:closed:hover,
QTreeView::branch:closed:has-children:has-siblings:hover {
- border-image: none;
- image: url("GuiConfigs/list_arrow_blue.png");
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_blue.png");
}
QTreeView::branch:open:has-children:!has-siblings:hover,
-QTreeView::branch:open:has-children:has-siblings:hover {
- border-image: none;
- image: url("GuiConfigs/list_arrow_down_blue.png");
+QTreeView::branch:open:has-children:has-siblings:hover {
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_down_blue.png");
+}
+
+/* Game Grid */
+#game_list_grid_item[selected="true"] {
+ background: #354454;
+}
+#game_list_grid_item:hover {
+ background: #354454;
+}
+#game_list_grid_item:focus {
+ background: #0078D7;
+}
+
+/* Game Grid Font */
+#game_list_grid_item #game_list_grid_item_title_label {
+ font-weight: 600;
+ font-size: 8pt;
+ font-family: Lucida Grande;
+ color: #FFF;
+}
+
+/* Game Grid hover and focus: we need to handle properties differently when using descendants */
+#game_list_grid_item[selected="true"] #game_list_grid_item_title_label {
+ background-color: #354454;
+}
+#game_list_grid_item[hover="true"] #game_list_grid_item_title_label {
+ background-color: #354454;
+}
+#game_list_grid_item[focus="true"] #game_list_grid_item_title_label {
+ background-color: #0078D7;
}
diff --git a/bin/GuiConfigs/Nekotekina by GooseWing.qss b/bin/GuiConfigs/Nekotekina by GooseWing.qss
index 24cb7064df..eba076ff55 100755
--- a/bin/GuiConfigs/Nekotekina by GooseWing.qss
+++ b/bin/GuiConfigs/Nekotekina by GooseWing.qss
@@ -367,21 +367,6 @@ QLabel#l_controller {
}
-/* Game Grid Font */
-QTableWidget#game_grid {
- font-weight: 600;
- color: #ffd785;
- text-transform: uppercase;
- selection-color: #ffd785;
-}
-QTableWidget#game_grid::item:selected:active {
- selection-background-color: #4d4940;
-}
-QTableWidget#game_grid::item:selected:!active {
- selection-background-color: #615c51;
-}
-
-
/* Debug UI Settings buttons */
QLabel#color_button {
background: transparent;
@@ -489,3 +474,33 @@ QWidget#trophy_notification_frame {
background-color: #b3ac98;
color: #4d4940;
}
+
+/* Game Grid */
+#game_list_grid_item[selected="true"] {
+ background: #615c51;
+}
+#game_list_grid_item:hover {
+ background: #615c51;
+}
+#game_list_grid_item:focus {
+ background: #4d4940;
+}
+
+/* Game Grid Font */
+#game_list_grid_item #game_list_grid_item_title_label {
+ font-weight: 600;
+ text-transform: uppercase;
+ color: #ffd785;
+}
+
+/* Game Grid hover and focus: we need to handle properties differently when using descendants */
+#game_list_grid_item[selected="true"] #game_list_grid_item_title_label {
+ background-color: #615c51;
+}
+#game_list_grid_item[hover="true"] #game_list_grid_item_title_label {
+ background-color: #615c51;
+}
+#game_list_grid_item[focus="true"] #game_list_grid_item_title_label {
+ background-color: #4d4940;
+ color: #ffd785;
+}
diff --git a/bin/GuiConfigs/Skyline (Nightfall).qss b/bin/GuiConfigs/Skyline (Nightfall).qss
index 5af056fdd3..5271643ac3 100644
--- a/bin/GuiConfigs/Skyline (Nightfall).qss
+++ b/bin/GuiConfigs/Skyline (Nightfall).qss
@@ -220,6 +220,17 @@ QScrollBar::up-arrow, QScrollBar::down-arrow, QScrollBar::up-button:vertical, QS
border: none;
}
+#game_list_grid QScrollBar {
+ width: 10px;
+ height: 3px;
+}
+#game_list_grid QScrollBar::handle {
+ background: #1c273d;
+}
+#game_list_grid QScrollBar::handle:hover, QScrollBar::handle:pressed {
+ background: #0074e7;
+}
+
/* Radio Buttons */
QRadioButton::indicator {
border: 0.0625em solid #455971;
@@ -663,24 +674,49 @@ QLabel#debugger_frame_pc {
/* Tree view changes*/
QTreeView::branch:has-children:!has-siblings:closed,
QTreeView::branch:closed:has-children:has-siblings {
- border-image: none;
- image: url("GuiConfigs/list_arrow_white.png");
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_white.png");
}
QTreeView::branch:open:has-children:!has-siblings,
-QTreeView::branch:open:has-children:has-siblings {
- border-image: none;
- image: url("GuiConfigs/list_arrow_down_white.png");
+QTreeView::branch:open:has-children:has-siblings {
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_down_white.png");
}
QTreeView::branch:has-children:!has-siblings:closed:hover,
QTreeView::branch:closed:has-children:has-siblings:hover {
- border-image: none;
- image: url("GuiConfigs/list_arrow_blue.png");
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_blue.png");
}
QTreeView::branch:open:has-children:!has-siblings:hover,
-QTreeView::branch:open:has-children:has-siblings:hover {
- border-image: none;
- image: url("GuiConfigs/list_arrow_down_blue.png");
+QTreeView::branch:open:has-children:has-siblings:hover {
+ border-image: none;
+ image: url("GuiConfigs/list_arrow_down_blue.png");
+}
+
+/* Game Grid */
+#game_list_grid_item[selected="true"] {
+ background: #0f1d36;
+}
+#game_list_grid_item:focus {
+ background: #0f1d36;
+}
+
+#game_list_grid_item #game_list_grid_item_title_label {
+ color: #FFFFFF;
+}
+
+/* Game Grid hover and focus: we need to handle properties differently when using descendants */
+#game_list_grid_item[selected="true"] #game_list_grid_item_title_label {
+ background-color: #0f1d36;
+ color: #2397ff;
+}
+#game_list_grid_item[hover="true"] #game_list_grid_item_title_label {
+ color: #2397ff;
+}
+#game_list_grid_item[focus="true"] #game_list_grid_item_title_label {
+ background-color: #0f1d36;
+ color: #2397ff;
}
diff --git a/bin/GuiConfigs/Skyline.qss b/bin/GuiConfigs/Skyline.qss
index e2de14a9f7..c1339e96ea 100644
--- a/bin/GuiConfigs/Skyline.qss
+++ b/bin/GuiConfigs/Skyline.qss
@@ -678,3 +678,24 @@ QLabel#debugger_frame_pc {
color: #000; /* Font Color: Black */
background-color: #00ff00; /* Green */
}
+
+/* Game Grid */
+#game_list_grid_item[selected="true"] {
+ background: #4343c1;
+}
+#game_list_grid_item:focus {
+ background: #4343c1;
+}
+
+/* Game Grid hover and focus: we need to handle properties differently when using descendants */
+#game_list_grid_item[selected="true"] #game_list_grid_item_title_label {
+ background-color: #4343c1;
+ color: #FFF;
+}
+#game_list_grid_item[hover="true"] #game_list_grid_item_title_label {
+ color: #4343c1;
+}
+#game_list_grid_item[focus="true"] #game_list_grid_item_title_label {
+ background-color: #4343c1;
+ color: #FFF;
+}
diff --git a/bin/GuiConfigs/YoRHa by Ani.qss b/bin/GuiConfigs/YoRHa by Ani.qss
index 527f9f25d0..c3345d4cd9 100644
--- a/bin/GuiConfigs/YoRHa by Ani.qss
+++ b/bin/GuiConfigs/YoRHa by Ani.qss
@@ -358,14 +358,6 @@ QLabel#l_controller {
color: #4d4940;
}
-/* Game Grid Font */
-QTableWidget#game_grid {
- font-weight: 600;
- color: #4d4940;
- text-transform: uppercase;
- selection-color: #aea993;
-}
-
/* Debug UI Settings buttons */
QLabel#color_button {
background: transparent;
@@ -463,3 +455,26 @@ QSlider#sizeSlider::groove:horizontal {
QLabel#validLabel {
font-weight: 600;
}
+
+/* Game Grid */
+#game_list_grid_item[selected="true"] {
+ background: #4d4940;
+}
+#game_list_grid_item:focus {
+ background: #4d4940;
+}
+
+/* Game Grid Font */
+#game_list_grid_item #game_list_grid_item_title_label {
+ font-weight: 600;
+ color: #4d4940;
+ text-transform: uppercase;
+}
+
+/* Game Grid hover and focus: we need to handle properties differently when using descendants */
+#game_list_grid_item[selected="true"] #game_list_grid_item_title_label {
+ color: #aea993;
+}
+#game_list_grid_item[focus="true"] #game_list_grid_item_title_label {
+ color: #aea993;
+}
diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj
index 817038040a..eb20fe5be9 100644
--- a/rpcs3/rpcs3.vcxproj
+++ b/rpcs3/rpcs3.vcxproj
@@ -234,6 +234,12 @@
true
+
+ true
+
+
+ true
+
true
@@ -246,6 +252,12 @@
true
+
+ true
+
+
+ true
+
true
@@ -348,6 +360,9 @@
true
+
+ true
+
true
@@ -471,6 +486,12 @@
true
+
+ true
+
+
+ true
+
true
@@ -483,6 +504,12 @@
true
+
+ true
+
+
+ true
+
true
@@ -585,6 +612,9 @@
true
+
+ true
+
true
@@ -667,8 +697,12 @@
+
+
+
+
@@ -676,6 +710,7 @@
+
@@ -691,6 +726,7 @@
+
@@ -713,7 +749,6 @@
-
@@ -1148,6 +1183,16 @@
+ Moc%27ing %(Identity)...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DMINIUPNP_STATICLIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"
+ Moc%27ing %(Identity)...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DMINIUPNP_STATICLIB -DHAVE_SDL2 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\libsdl-org\SDL\include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"
+ $(QTDIR)\bin\moc.exe;%(FullPath)
+ $(QTDIR)\bin\moc.exe;%(FullPath)
+
+
@@ -1161,7 +1206,37 @@
+
+ $(QTDIR)\bin\moc.exe;%(FullPath)
+ Moc%27ing %(Identity)...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DMINIUPNP_STATICLIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"
+ $(QTDIR)\bin\moc.exe;%(FullPath)
+ Moc%27ing %(Identity)...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DMINIUPNP_STATICLIB -DHAVE_SDL2 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\libsdl-org\SDL\include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"
+
+
+ $(QTDIR)\bin\moc.exe;%(FullPath)
+ Moc%27ing %(Identity)...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DMINIUPNP_STATICLIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"
+ $(QTDIR)\bin\moc.exe;%(FullPath)
+ Moc%27ing %(Identity)...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DMINIUPNP_STATICLIB -DHAVE_SDL2 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\libsdl-org\SDL\include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"
+
+
+ $(QTDIR)\bin\moc.exe;%(FullPath)
+ Moc%27ing %(Identity)...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DMINIUPNP_STATICLIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"
+ $(QTDIR)\bin\moc.exe;%(FullPath)
+ Moc%27ing %(Identity)...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DMINIUPNP_STATICLIB -DHAVE_SDL2 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\libsdl-org\SDL\include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"
+
$(QTDIR)\bin\moc.exe;%(FullPath)
@@ -1214,6 +1289,7 @@
"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"
+
Moc%27ing %(Identity)...
@@ -1310,6 +1386,16 @@
.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"
+
+ $(QTDIR)\bin\moc.exe;%(FullPath)
+ Moc%27ing %(Identity)...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DMINIUPNP_STATICLIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"
+ $(QTDIR)\bin\moc.exe;%(FullPath)
+ Moc%27ing %(Identity)...
+ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
+ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DMINIUPNP_STATICLIB -DHAVE_SDL2 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\libsdl-org\SDL\include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"
+
@@ -1416,7 +1502,6 @@
.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DMINIUPNP_STATICLIB -DHAVE_SDL2 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\libsdl-org\SDL\include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"
-
@@ -1752,6 +1837,17 @@
"$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)"
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters
index e961d160d2..5369594056 100644
--- a/rpcs3/rpcs3.vcxproj.filters
+++ b/rpcs3/rpcs3.vcxproj.filters
@@ -154,6 +154,9 @@
{9191ae51-8b80-4606-b5bc-966a9588f538}
+
+ {b927140a-5214-4628-a648-8380ae793179}
+
@@ -381,9 +384,6 @@
Gui\game list
-
- Gui\game list
-
Gui\dev tools
@@ -954,6 +954,54 @@
Gui\flow_layout
+
+ Gui\game list
+
+
+ Generated Files\Debug
+
+
+ Generated Files\Release
+
+
+ Gui\game list
+
+
+ Gui\custom items
+
+
+ Gui\game list
+
+
+ Generated Files\Debug
+
+
+ Generated Files\Release
+
+
+ Gui\flow_layout
+
+
+ Generated Files\Debug
+
+
+ Generated Files\Release
+
+
+ Gui\screenshot manager
+
+
+ Generated Files\Debug
+
+
+ Generated Files\Release
+
+
+ Generated Files\Debug
+
+
+ Generated Files\Release
+
@@ -992,9 +1040,6 @@
Gui\game window
-
- Gui\game list
-
Io\evdev
@@ -1130,6 +1175,9 @@
Gui\flow_layout
+
+ Gui\custom items
+
@@ -1408,6 +1456,21 @@
Gui\flow_layout
+
+ Gui\game list
+
+
+ Gui\game list
+
+
+ Gui\game list
+
+
+ Gui\screenshot manager
+
+
+ Gui\flow_layout
+
@@ -1415,4 +1478,33 @@
+
+
+ StyleSheets
+
+
+ StyleSheets
+
+
+ StyleSheets
+
+
+ StyleSheets
+
+
+ StyleSheets
+
+
+ StyleSheets
+
+
+ StyleSheets
+
+
+ StyleSheets
+
+
+ StyleSheets
+
+
\ No newline at end of file
diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt
index 6262889249..fab0863724 100644
--- a/rpcs3/rpcs3qt/CMakeLists.txt
+++ b/rpcs3/rpcs3qt/CMakeLists.txt
@@ -24,12 +24,15 @@ set(SRC_FILES
find_dialog.cpp
flow_layout.cpp
flow_widget.cpp
+ flow_widget_item.cpp
game_compatibility.cpp
game_list.cpp
+ game_list_base.cpp
game_list_delegate.cpp
game_list_frame.cpp
game_list_grid.cpp
- game_list_grid_delegate.cpp
+ game_list_grid_item.cpp
+ game_list_table.cpp
gui_application.cpp
gl_gs_frame.cpp
gs_frame.cpp
@@ -47,6 +50,7 @@ set(SRC_FILES
memory_viewer_panel.cpp
microphone_creator.cpp
movie_item.cpp
+ movie_item_base.cpp
msg_dialog_frame.cpp
osk_dialog_frame.cpp
pad_led_settings_dialog.cpp
@@ -73,6 +77,7 @@ set(SRC_FILES
save_data_info_dialog.cpp
save_data_list_dialog.cpp
save_manager_dialog.cpp
+ screenshot_item.cpp
screenshot_manager_dialog.cpp
screenshot_preview.cpp
sendmessage_dialog_frame.cpp
diff --git a/rpcs3/rpcs3qt/flow_layout.cpp b/rpcs3/rpcs3qt/flow_layout.cpp
index 698999dbed..730f0c6a18 100644
--- a/rpcs3/rpcs3qt/flow_layout.cpp
+++ b/rpcs3/rpcs3qt/flow_layout.cpp
@@ -51,14 +51,23 @@
#include
#include "flow_layout.h"
-flow_layout::flow_layout(QWidget* parent, int margin, int hSpacing, int vSpacing)
- : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
+flow_layout::flow_layout(QWidget* parent, int margin, bool dynamic_spacing, int hSpacing, int vSpacing)
+ : QLayout(parent)
+ , m_dynamic_spacing(dynamic_spacing)
+ , m_hSpaceInitial(hSpacing)
+ , m_vSpaceInitial(vSpacing)
+ , m_hSpace(hSpacing)
+ , m_vSpace(vSpacing)
{
setContentsMargins(margin, margin, margin, margin);
}
-flow_layout::flow_layout(int margin, int hSpacing, int vSpacing)
- : m_hSpace(hSpacing), m_vSpace(vSpacing)
+flow_layout::flow_layout(int margin, bool dynamic_spacing, int hSpacing, int vSpacing)
+ : m_dynamic_spacing(dynamic_spacing)
+ , m_hSpaceInitial(hSpacing)
+ , m_vSpaceInitial(vSpacing)
+ , m_hSpace(hSpacing)
+ , m_vSpace(vSpacing)
{
setContentsMargins(margin, margin, margin, margin);
}
@@ -76,10 +85,12 @@ void flow_layout::clear()
delete item;
}
itemList.clear();
+ m_positions.clear();
}
void flow_layout::addItem(QLayoutItem* item)
{
+ m_positions.append(position{ .row = -1, .col = -1 });
itemList.append(item);
}
@@ -116,7 +127,10 @@ QLayoutItem* flow_layout::itemAt(int index) const
QLayoutItem* flow_layout::takeAt(int index)
{
if (index >= 0 && index < itemList.size())
+ {
+ m_positions.takeAt(index);
return itemList.takeAt(index);
+ }
return nullptr;
}
@@ -167,13 +181,57 @@ int flow_layout::doLayout(const QRect& rect, bool testOnly) const
{
int left, top, right, bottom;
getContentsMargins(&left, &top, &right, &bottom);
- QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
+ const QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
int x = effectiveRect.x();
int y = effectiveRect.y();
int lineHeight = 0;
+ int rows = 0;
+ int cols = 0;
- for (QLayoutItem* item : qAsConst(itemList))
+ if (m_dynamic_spacing)
{
+ const int available_width = effectiveRect.width();
+ const int min_spacing = smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
+ bool fits_into_width = true;
+ int width = 0;
+ int index = 0;
+
+ for (; index < itemList.size(); index++)
+ {
+ if (QLayoutItem* item = itemList.at(index))
+ {
+ const int new_width = width + item->sizeHint().width() + (width > 0 ? min_spacing : 0);
+
+ if (new_width > effectiveRect.width())
+ {
+ fits_into_width = false;
+ break;
+ }
+
+ width = new_width;
+ }
+ }
+
+ // Try to evenly distribute the items across the width
+ m_hSpace = (index == 0) ? -1 : ((available_width - width) / index);
+
+ if (fits_into_width)
+ {
+ // Make sure there aren't huge gaps between the items
+ m_hSpace = smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
+ }
+ }
+ else
+ {
+ m_hSpace = m_hSpaceInitial;
+ }
+
+ for (int i = 0, row = 0, col = 0; i < itemList.size(); i++)
+ {
+ QLayoutItem* item = itemList.at(i);
+ if (!item)
+ continue;
+
const QWidget* wid = item->widget();
if (!wid)
continue;
@@ -193,20 +251,33 @@ int flow_layout::doLayout(const QRect& rect, bool testOnly) const
y = y + lineHeight + spaceY;
nextX = x + item->sizeHint().width() + spaceX;
lineHeight = 0;
+ col = 0;
+ row++;
}
+ position& pos = m_positions[i];
+ pos.row = row;
+ pos.col = col++;
+
+ rows = std::max(rows, pos.row + 1);
+ cols = std::max(cols, pos.col + 1);
+
if (!testOnly)
item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
x = nextX;
lineHeight = qMax(lineHeight, item->sizeHint().height());
}
+
+ m_rows = rows;
+ m_cols = cols;
+
return y + lineHeight - rect.y() + bottom;
}
int flow_layout::smartSpacing(QStyle::PixelMetric pm) const
{
- QObject* parent = this->parent();
+ const QObject* parent = this->parent();
if (!parent)
{
return -1;
@@ -214,9 +285,9 @@ int flow_layout::smartSpacing(QStyle::PixelMetric pm) const
if (parent->isWidgetType())
{
- QWidget* pw = static_cast(parent);
+ const QWidget* pw = static_cast(parent);
return pw->style()->pixelMetric(pm, nullptr, pw);
}
- return static_cast(parent)->spacing();
+ return static_cast(parent)->spacing();
}
diff --git a/rpcs3/rpcs3qt/flow_layout.h b/rpcs3/rpcs3qt/flow_layout.h
index b395b2cbcc..e8a4df65bd 100644
--- a/rpcs3/rpcs3qt/flow_layout.h
+++ b/rpcs3/rpcs3qt/flow_layout.h
@@ -57,15 +57,23 @@
class flow_layout : public QLayout
{
public:
- explicit flow_layout(QWidget* parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
- explicit flow_layout(int margin = -1, int hSpacing = -1, int vSpacing = -1);
+ struct position
+ {
+ int row{};
+ int col{};
+ };
+
+ explicit flow_layout(QWidget* parent, int margin = -1, bool dynamic_spacing = false, int hSpacing = -1, int vSpacing = -1);
+ explicit flow_layout(int margin = -1, bool dynamic_spacing = false, int hSpacing = -1, int vSpacing = -1);
~flow_layout();
void clear();
+ const QList& item_list() const { return itemList; }
+ const QList& positions() const { return m_positions; }
+ int rows() const { return m_rows; }
+ int cols() const { return m_cols; }
void addItem(QLayoutItem* item) override;
- int horizontalSpacing() const;
- int verticalSpacing() const;
Qt::Orientations expandingDirections() const override;
bool hasHeightForWidth() const override;
int heightForWidth(int) const override;
@@ -77,10 +85,19 @@ public:
QLayoutItem* takeAt(int index) override;
private:
+ int horizontalSpacing() const;
+ int verticalSpacing() const;
int doLayout(const QRect& rect, bool testOnly) const;
int smartSpacing(QStyle::PixelMetric pm) const;
QList itemList;
- int m_hSpace;
- int m_vSpace;
+ bool m_dynamic_spacing{};
+ int m_hSpaceInitial{-1};
+ int m_vSpaceInitial{-1};
+ mutable int m_hSpace{-1};
+ mutable int m_vSpace{-1};
+
+ mutable QList m_positions;
+ mutable int m_rows{};
+ mutable int m_cols{};
};
diff --git a/rpcs3/rpcs3qt/flow_widget.cpp b/rpcs3/rpcs3qt/flow_widget.cpp
index 281b8e9ee7..10670f5f8f 100644
--- a/rpcs3/rpcs3qt/flow_widget.cpp
+++ b/rpcs3/rpcs3qt/flow_widget.cpp
@@ -1,8 +1,10 @@
+#include "stdafx.h"
#include "flow_widget.h"
-#include "flow_layout.h"
#include
#include
+#include
+#include
flow_widget::flow_widget(QWidget* parent)
: QWidget(parent)
@@ -11,13 +13,15 @@ flow_widget::flow_widget(QWidget* parent)
QWidget* widget = new QWidget(this);
widget->setLayout(m_flow_layout);
+ widget->setObjectName("flow_widget_content");
+ widget->setFocusProxy(this);
- QScrollArea* scrollArea = new QScrollArea(this);
- scrollArea->setWidget(widget);
- scrollArea->setWidgetResizable(true);
+ m_scroll_area = new QScrollArea(this);
+ m_scroll_area->setWidget(widget);
+ m_scroll_area->setWidgetResizable(true);
QVBoxLayout* layout = new QVBoxLayout(this);
- layout->addWidget(scrollArea);
+ layout->addWidget(m_scroll_area);
layout->setContentsMargins(0, 0, 0, 0);
setLayout(layout);
}
@@ -32,6 +36,9 @@ void flow_widget::add_widget(flow_widget_item* widget)
{
m_widgets << widget;
m_flow_layout->addWidget(widget);
+
+ connect(widget, &flow_widget_item::navigate, this, &flow_widget::on_navigate);
+ connect(widget, &flow_widget_item::focused, this, &flow_widget::on_item_focus);
}
}
@@ -46,19 +53,192 @@ QList& flow_widget::items()
return m_widgets;
}
-void flow_widget_item::paintEvent(QPaintEvent* event)
+flow_widget_item* flow_widget::selected_item() const
{
- QWidget::paintEvent(event);
-
- if (!got_visible && cb_on_first_visibility)
+ if (m_selected_index >= 0 && m_selected_index < m_widgets.size())
{
- if (QWidget* widget = static_cast(parent()))
+ return ::at32(m_widgets, m_selected_index);
+ }
+
+ return nullptr;
+}
+
+QScrollArea* flow_widget::scroll_area() const
+{
+ return m_scroll_area;
+}
+
+void flow_widget::paintEvent(QPaintEvent* /*event*/)
+{
+ // Needed for stylesheets to apply to QWidgets
+ QStyleOption option;
+ option.initFrom(this);
+ QPainter painter(this);
+ style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this);
+}
+
+int flow_widget::find_item(const flow_layout::position& pos)
+{
+ if (pos.row < 0 || pos.col < 0)
+ {
+ return -1;
+ }
+
+ const auto& positions = m_flow_layout->positions();
+
+ for (int i = 0; i < positions.size(); i++)
+ {
+ if (const auto& other = ::at32(positions, i); other.row == pos.row && other.col == pos.col)
{
- if (widget->visibleRegion().intersects(geometry()))
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+ flow_layout::position flow_widget::find_item(flow_widget_item* item)
+{
+ if (item)
+ {
+ const auto& item_list = m_flow_layout->item_list();
+ const auto& positions = m_flow_layout->positions();
+ ensure(item_list.size() == positions.size());
+
+ for (int i = 0; i < item_list.size(); i++)
+ {
+ if (const auto& layout_item = ::at32(item_list, i); layout_item && layout_item->widget() == item)
{
- got_visible = true;
- cb_on_first_visibility();
+ return ::at32(positions, i);
}
}
}
+
+ return flow_layout::position{ .row = -1, .col = - 1};
+}
+
+flow_layout::position flow_widget::find_next_item(flow_layout::position current_pos, flow_navigation value)
+{
+ if (current_pos.row >= 0 && current_pos.col >= 0 && m_flow_layout->rows() > 0 && m_flow_layout->cols() > 0)
+ {
+ switch (value)
+ {
+ case flow_navigation::up:
+ // Go up one row.
+ if (current_pos.row > 0)
+ {
+ current_pos.row--;
+ }
+ break;
+ case flow_navigation::down:
+ // Go down one row. Beware of last row which might have less columns.
+ for (const auto& pos : m_flow_layout->positions())
+ {;
+ if (pos.col != current_pos.col) continue;
+ if (pos.row == current_pos.row + 1)
+ {
+ current_pos.row = pos.row;
+ break;
+ }
+ }
+ break;
+ case flow_navigation::left:
+ // Go left one column.
+ if (current_pos.col > 0)
+ {
+ current_pos.col--;
+ }
+ break;
+ case flow_navigation::right:
+ // Go right one column. Beware of last row which might have less columns.
+ for (const auto& pos : m_flow_layout->positions())
+ {
+ if (pos.row > current_pos.row) break;
+ if (pos.row < current_pos.row) continue;
+ if (pos.col == current_pos.col + 1)
+ {
+ current_pos.col = pos.col;
+ break;
+ }
+ }
+ break;
+ case flow_navigation::home:
+ // Go to leftmost column.
+ current_pos.col = 0;
+ break;
+ case flow_navigation::end:
+ // Go to last column. Beware of last row which might have less columns.
+ for (const auto& pos : m_flow_layout->positions())
+ {
+ if (pos.row > current_pos.row) break;
+ if (pos.row < current_pos.row) continue;
+ current_pos.col = std::max(current_pos.col, pos.col);
+ }
+ break;
+ case flow_navigation::page_up:
+ // Go to top row.
+ current_pos.row = 0;
+ break;
+ case flow_navigation::page_down:
+ // Go to bottom row. Beware of last row which might have less columns.
+ for (const auto& pos : m_flow_layout->positions())
+ {
+ if (pos.col != current_pos.col) continue;
+ current_pos.row = std::max(current_pos.row, pos.row);
+ }
+ break;
+ }
+ }
+
+ return current_pos;
+}
+
+void flow_widget::select_item(flow_widget_item* item)
+{
+ const flow_layout::position selected_pos = find_item(item);
+ const int selected_index = find_item(selected_pos);
+
+ if (selected_index < 0 || selected_index >= items().size())
+ {
+ m_selected_index = -1;
+ return;
+ }
+
+ m_selected_index = selected_index;
+ Q_EMIT ItemSelectionChanged(m_selected_index);
+
+ for (int i = 0; i < items().size(); i++)
+ {
+ if (flow_widget_item* item = items().at(i))
+ {
+ // We need to polish the widgets in order to re-apply any stylesheet changes for the selected property.
+ item->selected = i == m_selected_index;
+ item->polish_style();
+ }
+ }
+
+ // Make sure we see the focused widget
+ m_scroll_area->ensureWidgetVisible(::at32(items(), m_selected_index));
+}
+
+void flow_widget::on_item_focus()
+{
+ select_item(static_cast(QObject::sender()));
+}
+
+void flow_widget::on_navigate(flow_navigation value)
+{
+ const flow_layout::position selected_pos = find_next_item(find_item(static_cast(QObject::sender())), value);
+ const int selected_index = find_item(selected_pos);
+ if (selected_index < 0 || selected_index >= items().size())
+ {
+ return;
+ }
+
+ if (flow_widget_item* item = items().at(selected_index))
+ {
+ item->setFocus();
+ }
+
+ m_selected_index = selected_index;
}
diff --git a/rpcs3/rpcs3qt/flow_widget.h b/rpcs3/rpcs3qt/flow_widget.h
index 5389f6cfed..a0166b4dbb 100644
--- a/rpcs3/rpcs3qt/flow_widget.h
+++ b/rpcs3/rpcs3qt/flow_widget.h
@@ -1,21 +1,16 @@
#pragma once
+#include "flow_widget_item.h"
+#include "flow_layout.h"
+
#include
-
-class flow_layout;
-
-class flow_widget_item : public QWidget
-{
-public:
- using QWidget::QWidget;
- void paintEvent(QPaintEvent* event) override;
-
- bool got_visible{};
- std::function cb_on_first_visibility{};
-};
+#include
+#include
class flow_widget : public QWidget
{
+ Q_OBJECT
+
public:
flow_widget(QWidget* parent);
virtual ~flow_widget();
@@ -24,8 +19,28 @@ public:
void clear();
QList& items();
+ flow_widget_item* selected_item() const;
+ QScrollArea* scroll_area() const;
+
+ void paintEvent(QPaintEvent* event) override;
+
+Q_SIGNALS:
+ void ItemSelectionChanged(int index);
+
+private Q_SLOTS:
+ void on_item_focus();
+ void on_navigate(flow_navigation value);
+
+protected:
+ void select_item(flow_widget_item* item);
private:
+ int find_item(const flow_layout::position& pos);
+ flow_layout::position find_item(flow_widget_item* item);
+ flow_layout::position find_next_item(flow_layout::position current_pos, flow_navigation value);
+
flow_layout* m_flow_layout{};
+ QScrollArea* m_scroll_area{};
QList m_widgets{};
+ int m_selected_index = -1;
};
diff --git a/rpcs3/rpcs3qt/flow_widget_item.cpp b/rpcs3/rpcs3qt/flow_widget_item.cpp
new file mode 100644
index 0000000000..cb1c607c42
--- /dev/null
+++ b/rpcs3/rpcs3qt/flow_widget_item.cpp
@@ -0,0 +1,116 @@
+#include "flow_widget_item.h"
+
+#include
+#include
+#include
+
+void flow_widget_item::polish_style()
+{
+ style()->unpolish(this);
+ style()->polish(this);
+}
+
+void flow_widget_item::paintEvent(QPaintEvent* /*event*/)
+{
+ // Needed for stylesheets to apply to QWidgets
+ QStyleOption option;
+ option.initFrom(this);
+ QPainter painter(this);
+ style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this);
+
+ if (!got_visible && cb_on_first_visibility)
+ {
+ if (QWidget* widget = static_cast(parent()))
+ {
+ if (widget->visibleRegion().intersects(geometry()))
+ {
+ got_visible = true;
+ cb_on_first_visibility();
+ }
+ }
+ }
+}
+
+void flow_widget_item::focusInEvent(QFocusEvent* event)
+{
+ QWidget::focusInEvent(event);
+
+ // We need to polish the widgets in order to re-apply any stylesheet changes for the focus property.
+ polish_style();
+
+ Q_EMIT focused();
+}
+
+void flow_widget_item::focusOutEvent(QFocusEvent* event)
+{
+ QWidget::focusOutEvent(event);
+
+ // We need to polish the widgets in order to re-apply any stylesheet changes for the focus property.
+ polish_style();
+}
+
+void flow_widget_item::keyPressEvent(QKeyEvent* event)
+{
+ if (!event)
+ {
+ return;
+ }
+
+ switch (event->key())
+ {
+ case Qt::Key_Left:
+ Q_EMIT navigate(flow_navigation::left);
+ return;
+ case Qt::Key_Right:
+ Q_EMIT navigate(flow_navigation::right);
+ return;
+ case Qt::Key_Up:
+ Q_EMIT navigate(flow_navigation::up);
+ return;
+ case Qt::Key_Down:
+ Q_EMIT navigate(flow_navigation::down);
+ return;
+ case Qt::Key_Home:
+ Q_EMIT navigate(flow_navigation::home);
+ return;
+ case Qt::Key_End:
+ Q_EMIT navigate(flow_navigation::end);
+ return;
+ case Qt::Key_PageUp:
+ Q_EMIT navigate(flow_navigation::page_up);
+ return;
+ case Qt::Key_PageDown:
+ Q_EMIT navigate(flow_navigation::page_down);
+ return;
+ default:
+ break;
+ }
+
+ QWidget::keyPressEvent(event);
+}
+
+bool flow_widget_item::event(QEvent* event)
+{
+ bool hover_changed = false;
+
+ switch (event->type())
+ {
+ case QEvent::HoverEnter:
+ hover_changed = setProperty("hover", "true");
+ break;
+ case QEvent::HoverLeave:
+ hover_changed = setProperty("hover", "false");
+ break;
+ default:
+ break;
+ }
+
+ if (hover_changed)
+ {
+ // We need to polish the widgets in order to re-apply any stylesheet changes for the custom hover property.
+ // :hover does not work if we add descendants in the qss, so we need to use a custom property.
+ polish_style();
+ }
+
+ return QWidget::event(event);
+}
diff --git a/rpcs3/rpcs3qt/flow_widget_item.h b/rpcs3/rpcs3qt/flow_widget_item.h
new file mode 100644
index 0000000000..28c7afe378
--- /dev/null
+++ b/rpcs3/rpcs3qt/flow_widget_item.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include
+#include
+
+#include
+
+enum class flow_navigation
+{
+ up,
+ down,
+ left,
+ right,
+ home,
+ end,
+ page_up,
+ page_down
+};
+
+class flow_widget_item : public QWidget
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool hover MEMBER m_hover) // Stylesheet workaround for descendants with parent pseudo state
+ Q_PROPERTY(bool selected MEMBER selected) // Stylesheet workaround for descendants with parent pseudo state
+
+public:
+ using QWidget::QWidget;
+
+ virtual void polish_style();
+
+ void paintEvent(QPaintEvent* event) override;
+ void focusInEvent(QFocusEvent* event) override;
+ void focusOutEvent(QFocusEvent* event) override;
+ void keyPressEvent(QKeyEvent* event) override;
+ bool event(QEvent* event) override;
+
+ bool got_visible{};
+ bool selected{};
+ std::function cb_on_first_visibility{};
+
+protected:
+ bool m_hover{};
+
+Q_SIGNALS:
+ void navigate(flow_navigation value);
+ void focused();
+};
diff --git a/rpcs3/rpcs3qt/game_list.cpp b/rpcs3/rpcs3qt/game_list.cpp
index 979198649a..b7b22eefa5 100644
--- a/rpcs3/rpcs3qt/game_list.cpp
+++ b/rpcs3/rpcs3qt/game_list.cpp
@@ -1,6 +1,15 @@
+#include "stdafx.h"
#include "game_list.h"
#include "movie_item.h"
+game_list::game_list() : QTableWidget(), game_list_base()
+{
+ m_icon_ready_callback = [this](const game_info& game)
+ {
+ Q_EMIT IconReady(game);
+ };
+}
+
void game_list::clear_list()
{
m_last_hover_item = nullptr;
diff --git a/rpcs3/rpcs3qt/game_list.h b/rpcs3/rpcs3qt/game_list.h
index ea09b2790d..983ec6490e 100644
--- a/rpcs3/rpcs3qt/game_list.h
+++ b/rpcs3/rpcs3qt/game_list.h
@@ -3,46 +3,31 @@
#include
#include
#include
-#include
-#include "game_compatibility.h"
-#include "Emu/GameInfo.h"
+#include "game_list_base.h"
+#include "util/atomic.hpp"
class movie_item;
-/* Having the icons associated with the game info simplifies logic internally */
-struct gui_game_info
-{
- GameInfo info{};
- QString localized_category;
- compat::status compat;
- QPixmap icon;
- QPixmap pxmap;
- bool hasCustomConfig = false;
- bool hasCustomPadConfig = false;
- bool has_hover_gif = false;
- movie_item* item = nullptr;
-};
-
-typedef std::shared_ptr game_info;
-Q_DECLARE_METATYPE(game_info)
-
/*
class used in order to get deselection and hover change
if you know a simpler way, tell @Megamouse
*/
-class game_list : public QTableWidget
+class game_list : public QTableWidget, public game_list_base
{
Q_OBJECT
public:
- void clear_list(); // Use this instead of clearContents
+ game_list();
+
+ void clear_list() override; // Use this instead of clearContents
public Q_SLOTS:
void FocusAndSelectFirstEntryIfNoneIs();
Q_SIGNALS:
void FocusToSearchBar();
+ void IconReady(const game_info& game);
protected:
movie_item* m_last_hover_item = nullptr;
diff --git a/rpcs3/rpcs3qt/game_list_base.cpp b/rpcs3/rpcs3qt/game_list_base.cpp
new file mode 100644
index 0000000000..4dae7ea8e3
--- /dev/null
+++ b/rpcs3/rpcs3qt/game_list_base.cpp
@@ -0,0 +1,227 @@
+#include "stdafx.h"
+#include "game_list_base.h"
+#include "localized.h"
+
+#include
+
+#include
+#include
+
+LOG_CHANNEL(game_list_log, "GameList");
+
+game_list_base::game_list_base()
+{
+}
+
+void game_list_base::repaint_icons(QList& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio)
+{
+ m_icon_size = icon_size;
+ m_icon_color = icon_color;
+
+ QPixmap placeholder(icon_size * device_pixel_ratio);
+ placeholder.setDevicePixelRatio(device_pixel_ratio);
+ placeholder.fill(Qt::transparent);
+
+ for (game_info& game : game_data)
+ {
+ game->pxmap = placeholder;
+
+ if (movie_item_base* item = game->item)
+ {
+ item->set_icon_load_func([this, game, device_pixel_ratio, cancel = item->icon_loading_aborted()](int)
+ {
+ IconLoadFunction(game, device_pixel_ratio, cancel);
+ });
+
+ item->call_icon_func();
+ }
+ }
+}
+
+void game_list_base::IconLoadFunction(game_info game, qreal device_pixel_ratio, std::shared_ptr> cancel)
+{
+ if (cancel && cancel->load())
+ {
+ return;
+ }
+
+ static std::unordered_set warn_once_list;
+ static shared_mutex s_mtx;
+
+ if (game->icon.isNull() && (game->info.icon_path.empty() || !game->icon.load(QString::fromStdString(game->info.icon_path))))
+ {
+ if (game_list_log.warning)
+ {
+ bool logged = false;
+ {
+ std::lock_guard lock(s_mtx);
+ logged = !warn_once_list.emplace(game->info.icon_path).second;
+ }
+
+ if (!logged)
+ {
+ game_list_log.warning("Could not load image from path %s", QDir(QString::fromStdString(game->info.icon_path)).absolutePath().toStdString());
+ }
+ }
+ }
+
+ if (!game->item || (cancel && cancel->load()))
+ {
+ return;
+ }
+
+ const QColor color = GetGridCompatibilityColor(game->compat.color);
+ {
+ std::lock_guard lock(game->item->pixmap_mutex);
+ game->pxmap = PaintedPixmap(game->icon, device_pixel_ratio, game->hasCustomConfig, game->hasCustomPadConfig, color);
+ }
+
+ if (!cancel || !cancel->load())
+ {
+ if (m_icon_ready_callback)
+ m_icon_ready_callback(game);
+ }
+}
+
+QPixmap game_list_base::PaintedPixmap(const QPixmap& icon, qreal device_pixel_ratio, bool paint_config_icon, bool paint_pad_config_icon, const QColor& compatibility_color) const
+{
+ QSize canvas_size(320, 176);
+ QSize icon_size(icon.size());
+ QPoint target_pos;
+
+ if (!icon.isNull())
+ {
+ // Let's upscale the original icon to at least fit into the outer rect of the size of PS3's ICON0.PNG
+ if (icon_size.width() < 320 || icon_size.height() < 176)
+ {
+ icon_size.scale(320, 176, Qt::KeepAspectRatio);
+ }
+
+ canvas_size = icon_size;
+
+ // Calculate the centered size and position of the icon on our canvas.
+ if (icon_size.width() != 320 || icon_size.height() != 176)
+ {
+ ensure(icon_size.height() > 0);
+ constexpr double target_ratio = 320.0 / 176.0; // aspect ratio 20:11
+
+ if ((icon_size.width() / static_cast(icon_size.height())) > target_ratio)
+ {
+ canvas_size.setHeight(std::ceil(icon_size.width() / target_ratio));
+ }
+ else
+ {
+ canvas_size.setWidth(std::ceil(icon_size.height() * target_ratio));
+ }
+
+ target_pos.setX(std::max(0, (canvas_size.width() - icon_size.width()) / 2.0));
+ target_pos.setY(std::max(0, (canvas_size.height() - icon_size.height()) / 2.0));
+ }
+ }
+
+ // Create a canvas large enough to fit our entire scaled icon
+ QPixmap canvas(canvas_size * device_pixel_ratio);
+ canvas.setDevicePixelRatio(device_pixel_ratio);
+ canvas.fill(m_icon_color);
+
+ // Create a painter for our canvas
+ QPainter painter(&canvas);
+ painter.setRenderHint(QPainter::SmoothPixmapTransform);
+
+ // Draw the icon onto our canvas
+ if (!icon.isNull())
+ {
+ painter.drawPixmap(target_pos.x(), target_pos.y(), icon_size.width(), icon_size.height(), icon);
+ }
+
+ // Draw config icons if necessary
+ if (!m_is_list_layout && (paint_config_icon || paint_pad_config_icon))
+ {
+ const int width = canvas_size.width() * 0.2;
+ const QPoint origin = QPoint(canvas_size.width() - width, 0);
+ QString icon_path;
+
+ if (paint_config_icon && paint_pad_config_icon)
+ {
+ icon_path = ":/Icons/combo_config_bordered.png";
+ }
+ else if (paint_config_icon)
+ {
+ icon_path = ":/Icons/custom_config.png";
+ }
+ else if (paint_pad_config_icon)
+ {
+ icon_path = ":/Icons/controllers.png";
+ }
+
+ QPixmap custom_config_icon(icon_path);
+ custom_config_icon.setDevicePixelRatio(device_pixel_ratio);
+ painter.drawPixmap(origin, custom_config_icon.scaled(QSize(width, width) * device_pixel_ratio, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation));
+ }
+
+ // Draw game compatibility icons if necessary
+ if (compatibility_color.isValid())
+ {
+ const int size = canvas_size.height() * 0.2;
+ const int spacing = canvas_size.height() * 0.05;
+ QColor copyColor = QColor(compatibility_color);
+ copyColor.setAlpha(215); // ~85% opacity
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.setBrush(QBrush(copyColor));
+ painter.setPen(QPen(Qt::black, std::max(canvas_size.width() / 320, canvas_size.height() / 176)));
+ painter.drawEllipse(spacing, spacing, size, size);
+ }
+
+ // Finish the painting
+ painter.end();
+
+ // Scale and return our final image
+ return canvas.scaled(m_icon_size * device_pixel_ratio, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation);
+}
+
+QColor game_list_base::GetGridCompatibilityColor(const QString& string) const
+{
+ if (m_draw_compat_status_to_grid && !m_is_list_layout)
+ {
+ return QColor(string);
+ }
+ return QColor();
+}
+
+std::string game_list_base::GetGameVersion(const game_info& game)
+{
+ if (game->info.app_ver == Localized().category.unknown.toStdString())
+ {
+ // Fall back to Disc/Pkg Revision
+ return game->info.version;
+ }
+
+ return game->info.app_ver;
+}
+
+QIcon game_list_base::GetCustomConfigIcon(const game_info& game)
+{
+ if (!game)
+ return {};
+
+ static const QIcon icon_combo_config_bordered(":/Icons/combo_config_bordered.png");
+ static const QIcon icon_custom_config(":/Icons/custom_config.png");
+ static const QIcon icon_controllers(":/Icons/controllers.png");
+
+ if (game->hasCustomConfig && game->hasCustomPadConfig)
+ {
+ return icon_combo_config_bordered;
+ }
+
+ if (game->hasCustomConfig)
+ {
+ return icon_custom_config;
+ }
+
+ if (game->hasCustomPadConfig)
+ {
+ return icon_controllers;
+ }
+
+ return {};
+}
diff --git a/rpcs3/rpcs3qt/game_list_base.h b/rpcs3/rpcs3qt/game_list_base.h
new file mode 100644
index 0000000000..2d39402281
--- /dev/null
+++ b/rpcs3/rpcs3qt/game_list_base.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include "movie_item_base.h"
+#include "game_compatibility.h"
+#include "Emu/GameInfo.h"
+
+#include
+#include
+#include
+
+/* Having the icons associated with the game info simplifies logic internally */
+struct gui_game_info
+{
+ GameInfo info{};
+ QString localized_category;
+ compat::status compat;
+ QPixmap icon;
+ QPixmap pxmap;
+ bool hasCustomConfig = false;
+ bool hasCustomPadConfig = false;
+ bool has_hover_gif = false;
+ movie_item_base* item = nullptr;
+};
+
+typedef std::shared_ptr game_info;
+Q_DECLARE_METATYPE(game_info)
+
+class game_list_base
+{
+public:
+ game_list_base();
+
+ virtual void clear_list(){};
+ virtual void populate(
+ [[maybe_unused]] const std::vector& game_data,
+ [[maybe_unused]] const QMap& notes_map,
+ [[maybe_unused]] const QMap& title_map,
+ [[maybe_unused]] const std::string& selected_item_id,
+ [[maybe_unused]] bool play_hover_movies){};
+
+ void set_icon_size(QSize size) { m_icon_size = std::move(size); }
+ void set_icon_color(QColor color) { m_icon_color = std::move(color); }
+ void set_draw_compat_status_to_grid(bool enabled) { m_draw_compat_status_to_grid = enabled; }
+
+ virtual void repaint_icons(QList& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio);
+
+ // Returns the visible version string in the game list
+ static std::string GetGameVersion(const game_info& game);
+
+ /** Sets the custom config icon. */
+ static QIcon GetCustomConfigIcon(const game_info& game);
+
+protected:
+ void IconLoadFunction(game_info game, qreal device_pixel_ratio, std::shared_ptr> cancel);
+ QPixmap PaintedPixmap(const QPixmap& icon, qreal device_pixel_ratio, bool paint_config_icon = false, bool paint_pad_config_icon = false, const QColor& compatibility_color = {}) const;
+ QColor GetGridCompatibilityColor(const QString& string) const;
+
+ std::function m_icon_ready_callback{};
+ bool m_draw_compat_status_to_grid{};
+ bool m_is_list_layout{};
+ QSize m_icon_size{};
+ QColor m_icon_color{};
+};
diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp
index 86f374b25d..57c98e61cd 100644
--- a/rpcs3/rpcs3qt/game_list_frame.cpp
+++ b/rpcs3/rpcs3qt/game_list_frame.cpp
@@ -2,7 +2,6 @@
#include "qt_utils.h"
#include "settings_dialog.h"
#include "pad_settings_dialog.h"
-#include "game_list_delegate.h"
#include "custom_table_widget_item.h"
#include "input_dialog.h"
#include "localized.h"
@@ -10,8 +9,10 @@
#include "persistent_settings.h"
#include "emu_settings.h"
#include "gui_settings.h"
-#include "game_list.h"
+#include "game_list_delegate.h"
+#include "game_list_table.h"
#include "game_list_grid.h"
+#include "game_list_grid_item.h"
#include "patch_manager_dialog.h"
#include "Emu/Memory/vm.h"
@@ -89,39 +90,26 @@ game_list_frame::game_list_frame(std::shared_ptr gui_settings, std
m_game_dock->setWindowFlags(Qt::Widget);
setWidget(m_game_dock);
- m_game_grid = new game_list_grid(QSize(), m_icon_color, m_margin_factor, m_text_factor, false);
+ m_game_grid = new game_list_grid();
+ m_game_grid->installEventFilter(this);
+ m_game_grid->scroll_area()->verticalScrollBar()->installEventFilter(this);
- m_game_list = new game_list();
- m_game_list->setShowGrid(false);
- m_game_list->setItemDelegate(new game_list_delegate(m_game_list));
- m_game_list->setEditTriggers(QAbstractItemView::NoEditTriggers);
- m_game_list->setSelectionBehavior(QAbstractItemView::SelectRows);
- m_game_list->setSelectionMode(QAbstractItemView::SingleSelection);
- m_game_list->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
- m_game_list->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
- m_game_list->verticalScrollBar()->installEventFilter(this);
- m_game_list->verticalScrollBar()->setSingleStep(20);
- m_game_list->horizontalScrollBar()->setSingleStep(20);
- m_game_list->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
- m_game_list->verticalHeader()->setVisible(false);
- m_game_list->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
- m_game_list->horizontalHeader()->setHighlightSections(false);
- m_game_list->horizontalHeader()->setSortIndicatorShown(true);
- m_game_list->horizontalHeader()->setStretchLastSection(true);
- m_game_list->horizontalHeader()->setDefaultSectionSize(150);
- m_game_list->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
- m_game_list->setContextMenuPolicy(Qt::CustomContextMenu);
- m_game_list->setAlternatingRowColors(true);
- m_game_list->installEventFilter(this);
- m_game_list->setColumnCount(gui::column_count);
- m_game_list->setMouseTracking(true);
+ m_game_list = new game_list_table(this, m_persistent_settings);
m_game_compat = new game_compatibility(m_gui_settings, this);
m_central_widget = new QStackedWidget(this);
m_central_widget->addWidget(m_game_list);
m_central_widget->addWidget(m_game_grid);
- m_central_widget->setCurrentWidget(m_is_list_layout ? m_game_list : m_game_grid);
+
+ if (m_is_list_layout)
+ {
+ m_central_widget->setCurrentWidget(m_game_list);
+ }
+ else
+ {
+ m_central_widget->setCurrentWidget(m_game_grid);
+ }
m_game_dock->setCentralWidget(m_central_widget);
@@ -173,25 +161,10 @@ game_list_frame::game_list_frame(std::shared_ptr gui_settings, std
m_serials.clear();
m_games.pop_all();
});
- connect(this, &game_list_frame::IconReady, this, [this](const game_info& game)
- {
- if (!game || !game->item) return;
- game->item->call_icon_func();
- });
- connect(this, &game_list_frame::SizeOnDiskReady, this, [this](const game_info& game)
- {
- if (!m_is_list_layout || !game || !game->item) return;
- if (QTableWidgetItem* size_item = m_game_list->item(game->item->row(), gui::column_dir_size))
- {
- const u64& game_size = game->info.size_on_disk;
- size_item->setText(game_size != umax ? gui::utils::format_byte_size(game_size) : tr("Unknown"));
- size_item->setData(Qt::UserRole, QVariant::fromValue(game_size));
- }
- });
connect(m_game_list, &QTableWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu);
connect(m_game_list, &QTableWidget::itemSelectionChanged, this, &game_list_frame::ItemSelectionChangedSlot);
- connect(m_game_list, &QTableWidget::itemDoubleClicked, this, &game_list_frame::doubleClickedSlot);
+ connect(m_game_list, &QTableWidget::itemDoubleClicked, this, QOverload::of(&game_list_frame::doubleClickedSlot));
connect(m_game_list->horizontalHeader(), &QHeaderView::sectionClicked, this, &game_list_frame::OnColClicked);
connect(m_game_list->horizontalHeader(), &QHeaderView::customContextMenuRequested, this, [this](const QPoint& pos)
@@ -201,9 +174,9 @@ game_list_frame::game_list_frame(std::shared_ptr gui_settings, std
configure->exec(m_game_list->horizontalHeader()->viewport()->mapToGlobal(pos));
});
- connect(m_game_grid, &QTableWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu);
- connect(m_game_grid, &QTableWidget::itemSelectionChanged, this, &game_list_frame::ItemSelectionChangedSlot);
- connect(m_game_grid, &QTableWidget::itemDoubleClicked, this, &game_list_frame::doubleClickedSlot);
+ connect(m_game_grid, &QWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu);
+ connect(m_game_grid, &game_list_grid::ItemSelectionChanged, this, &game_list_frame::NotifyGameSelection);
+ connect(m_game_grid, &game_list_grid::ItemDoubleClicked, this, QOverload::of(&game_list_frame::doubleClickedSlot));
connect(m_game_compat, &game_compatibility::DownloadStarted, this, [this]()
{
@@ -222,7 +195,7 @@ game_list_frame::game_list_frame(std::shared_ptr gui_settings, std
});
connect(m_game_list, &game_list::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar);
- connect(m_game_grid, &game_list::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar);
+ connect(m_game_grid, &game_list_grid::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar);
for (int col = 0; col < m_columnActs.count(); ++col)
{
@@ -249,7 +222,7 @@ game_list_frame::game_list_frame(std::shared_ptr gui_settings, std
if (checked) // handle hidden columns that have zero width after showing them (stuck between others)
{
- FixNarrowColumns();
+ m_game_list->fix_narrow_columns();
}
});
}
@@ -283,48 +256,6 @@ game_list_frame::~game_list_frame()
SaveSettings();
}
-void game_list_frame::FixNarrowColumns() const
-{
- QApplication::processEvents();
-
- // handle columns (other than the icon column) that have zero width after showing them (stuck between others)
- for (int col = 1; col < m_columnActs.count(); ++col)
- {
- if (m_game_list->isColumnHidden(col))
- {
- continue;
- }
-
- if (m_game_list->columnWidth(col) <= m_game_list->horizontalHeader()->minimumSectionSize())
- {
- m_game_list->setColumnWidth(col, m_game_list->horizontalHeader()->minimumSectionSize());
- }
- }
-}
-
-void game_list_frame::ResizeColumnsToContents(int spacing) const
-{
- if (!m_game_list)
- {
- return;
- }
-
- m_game_list->verticalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents);
- m_game_list->horizontalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents);
-
- // Make non-icon columns slighty bigger for better visuals
- for (int i = 1; i < m_game_list->columnCount(); i++)
- {
- if (m_game_list->isColumnHidden(i))
- {
- continue;
- }
-
- const int size = m_game_list->horizontalHeader()->sectionSize(i) + spacing;
- m_game_list->horizontalHeader()->resizeSection(i, size);
- }
-}
-
void game_list_frame::OnColClicked(int col)
{
if (col == gui::column_icon) return; // Don't "sort" icons.
@@ -342,11 +273,11 @@ void game_list_frame::OnColClicked(int col)
m_gui_settings->SetValue(gui::gl_sortAsc, m_col_sort_order == Qt::AscendingOrder);
m_gui_settings->SetValue(gui::gl_sortCol, col);
- SortGameList();
+ m_game_list->sort(m_game_data.count(), m_sort_column, m_col_sort_order);
}
// Get visibility of entries
-bool game_list_frame::IsEntryVisible(const game_info& game, bool search_fallback)
+bool game_list_frame::IsEntryVisible(const game_info& game, bool search_fallback) const
{
const auto matches_category = [&]()
{
@@ -363,78 +294,6 @@ bool game_list_frame::IsEntryVisible(const game_info& game, bool search_fallback
return is_visible && matches_category() && SearchMatchesApp(qstr(game->info.name), serial, search_fallback);
}
-void game_list_frame::SortGameList()
-{
- // Back-up old header sizes to handle unwanted column resize in case of zero search results
- const int old_row_count = m_game_list->rowCount();
- const int old_game_count = m_game_data.count();
-
- std::vector column_widths(m_game_list->columnCount());
- for (int i = 0; i < m_game_list->columnCount(); i++)
- {
- column_widths[i] = m_game_list->columnWidth(i);
- }
-
- // Sorting resizes hidden columns, so unhide them as a workaround
- std::vector columns_to_hide;
-
- for (int i = 0; i < m_game_list->columnCount(); i++)
- {
- if (m_game_list->isColumnHidden(i))
- {
- m_game_list->setColumnHidden(i, false);
- columns_to_hide.push_back(i);
- }
- }
-
- // Sort the list by column and sort order
- m_game_list->sortByColumn(m_sort_column, m_col_sort_order);
-
- // Hide columns again
- for (int col : columns_to_hide)
- {
- m_game_list->setColumnHidden(col, true);
- }
-
- // Don't resize the columns if no game is shown to preserve the header settings
- if (!m_game_list->rowCount())
- {
- for (int i = 0; i < m_game_list->columnCount(); i++)
- {
- m_game_list->setColumnWidth(i, column_widths[i]);
- }
-
- m_game_list->horizontalHeader()->setSectionResizeMode(gui::column_icon, QHeaderView::Fixed);
- return;
- }
-
- // Fixate vertical header and row height
- m_game_list->verticalHeader()->setMinimumSectionSize(m_icon_size.height());
- m_game_list->verticalHeader()->setMaximumSectionSize(m_icon_size.height());
- m_game_list->resizeRowsToContents();
-
- // Resize columns if the game list was empty before
- if (!old_row_count && !old_game_count)
- {
- ResizeColumnsToContents();
- }
- else
- {
- m_game_list->resizeColumnToContents(gui::column_icon);
- }
-
- // Fixate icon column
- m_game_list->horizontalHeader()->setSectionResizeMode(gui::column_icon, QHeaderView::Fixed);
-
- // Shorten the last section to remove horizontal scrollbar if possible
- m_game_list->resizeColumnToContents(gui::column_count - 1);
-}
-
-QString game_list_frame::GetLastPlayedBySerial(const QString& serial) const
-{
- return m_persistent_settings->GetLastPlayed(serial);
-}
-
std::string game_list_frame::GetCacheDirBySerial(const std::string& serial)
{
return rpcs3::utils::get_cache_dir() + serial;
@@ -574,11 +433,43 @@ void game_list_frame::Refresh(const bool from_drive, const bool scroll_after)
// Fill Game List / Game Grid
+ const std::string selected_item = CurrentSelectionPath();
+
+ // Release old data
+ for (const auto& game : m_game_data)
+ {
+ game->item = nullptr;
+ }
+
+ // Get list of matching apps
+ std::vector matching_apps;
+
+ for (const auto& app : m_game_data)
+ {
+ if (IsEntryVisible(app))
+ {
+ matching_apps.push_back(app);
+ }
+ }
+
+ // Fallback is not needed when at least one entry is visible
+ if (matching_apps.empty())
+ {
+ for (const auto& app : m_game_data)
+ {
+ if (IsEntryVisible(app, true))
+ {
+ matching_apps.push_back(app);
+ }
+ }
+ }
+
if (m_is_list_layout)
{
+ m_game_grid->clear_list();
const int scroll_position = m_game_list->verticalScrollBar()->value();
- PopulateGameList();
- SortGameList();
+ m_game_list->populate(matching_apps, m_notes, m_titles, selected_item, m_play_hover_movies);
+ m_game_list->sort(m_game_data.count(), m_sort_column, m_col_sort_order);
RepaintIcons();
if (scroll_after)
@@ -592,6 +483,8 @@ void game_list_frame::Refresh(const bool from_drive, const bool scroll_after)
}
else
{
+ m_game_list->clear_list();
+ m_game_grid->populate(matching_apps, m_notes, m_titles, selected_item, m_play_hover_movies);
RepaintIcons();
}
}
@@ -919,26 +812,7 @@ void game_list_frame::OnRefreshFinished()
if (!std::exchange(m_initial_refresh_done, true))
{
- // Resize to fit and get the ideal icon column width
- ResizeColumnsToContents();
- const int icon_column_width = m_game_list->columnWidth(gui::column_icon);
-
- // Restore header layout from last session
- const QByteArray state = m_gui_settings->GetValue(gui::gl_state).toByteArray();
- if (!m_game_list->horizontalHeader()->restoreState(state) && m_game_list->rowCount())
- {
- // Nothing to do
- }
-
- // Make sure no columns are squished
- FixNarrowColumns();
-
- // Make sure that the icon column is large enough for the actual items.
- // This is important if the list appeared as empty when closing the software before.
- m_game_list->horizontalHeader()->resizeSection(gui::column_icon, icon_column_width);
-
- // Save new header state
- m_game_list->horizontalHeader()->restoreState(m_game_list->horizontalHeader()->saveState());
+ m_game_list->restore_layout(m_gui_settings->GetValue(gui::gl_state).toByteArray());
}
if (m_progress_dialog_timer)
@@ -1000,8 +874,11 @@ void game_list_frame::doubleClickedSlot(QTableWidgetItem *item)
return;
}
- const game_info game = GetGameInfoByMode(item);
+ doubleClickedSlot(GetGameInfoByMode(item));
+}
+void game_list_frame::doubleClickedSlot(const game_info& game)
+{
if (!game)
{
return;
@@ -1022,10 +899,6 @@ void game_list_frame::ItemSelectionChangedSlot()
game = GetGameInfoByMode(item);
}
}
- else if (const auto item = m_game_grid->currentItem(); item && item->isSelected())
- {
- game = GetGameInfoByMode(item);
- }
Q_EMIT NotifyGameSelection(game);
}
@@ -1138,21 +1011,20 @@ void game_list_frame::CreateShortcuts(const game_info& gameinfo, const std::set<
void game_list_frame::ShowContextMenu(const QPoint &pos)
{
QPoint global_pos;
- QTableWidgetItem* item;
+ game_info gameinfo;
if (m_is_list_layout)
{
- item = m_game_list->item(m_game_list->indexAt(pos).row(), gui::column_icon);
+ QTableWidgetItem* item = m_game_list->item(m_game_list->indexAt(pos).row(), gui::column_icon);
global_pos = m_game_list->viewport()->mapToGlobal(pos);
+ gameinfo = GetGameInfoFromItem(item);
}
- else
+ else if (game_list_grid_item* item = static_cast(m_game_grid->selected_item()))
{
- const QModelIndex mi = m_game_grid->indexAt(pos);
- item = m_game_grid->item(mi.row(), mi.column());
- global_pos = m_game_grid->viewport()->mapToGlobal(pos);
+ gameinfo = item->game();
+ global_pos = m_game_grid->mapToGlobal(pos);
}
- game_info gameinfo = GetGameInfoFromItem(item);
if (!gameinfo)
{
return;
@@ -1614,10 +1486,10 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
{
if (game)
{
- games[game->info.serial].insert(game_list_frame::GetGameVersion(game));
+ games[game->info.serial].insert(game_list::GetGameVersion(game));
}
}
- patch_manager_dialog patch_manager(m_gui_settings, games, gameinfo->info.serial, GetGameVersion(gameinfo), this);
+ patch_manager_dialog patch_manager(m_gui_settings, games, gameinfo->info.serial, game_list::GetGameVersion(gameinfo), this);
patch_manager.exec();
});
connect(open_game_folder, &QAction::triggered, this, [current_game]()
@@ -2272,130 +2144,6 @@ void game_list_frame::BatchRemoveShaderCaches()
QApplication::beep();
}
-QPixmap game_list_frame::PaintedPixmap(const QPixmap& icon, bool paint_config_icon, bool paint_pad_config_icon, const QColor& compatibility_color) const
-{
- const qreal device_pixel_ratio = devicePixelRatioF();
- QSize canvas_size(320, 176);
- QSize icon_size(icon.size());
- QPoint target_pos;
-
- if (!icon.isNull())
- {
- // Let's upscale the original icon to at least fit into the outer rect of the size of PS3's ICON0.PNG
- if (icon_size.width() < 320 || icon_size.height() < 176)
- {
- icon_size.scale(320, 176, Qt::KeepAspectRatio);
- }
-
- canvas_size = icon_size;
-
- // Calculate the centered size and position of the icon on our canvas.
- if (icon_size.width() != 320 || icon_size.height() != 176)
- {
- ensure(icon_size.height() > 0);
- constexpr double target_ratio = 320.0 / 176.0; // aspect ratio 20:11
-
- if ((icon_size.width() / static_cast(icon_size.height())) > target_ratio)
- {
- canvas_size.setHeight(std::ceil(icon_size.width() / target_ratio));
- }
- else
- {
- canvas_size.setWidth(std::ceil(icon_size.height() * target_ratio));
- }
-
- target_pos.setX(std::max(0, (canvas_size.width() - icon_size.width()) / 2.0));
- target_pos.setY(std::max(0, (canvas_size.height() - icon_size.height()) / 2.0));
- }
- }
-
- // Create a canvas large enough to fit our entire scaled icon
- QPixmap canvas(canvas_size * device_pixel_ratio);
- canvas.setDevicePixelRatio(device_pixel_ratio);
- canvas.fill(m_icon_color);
-
- // Create a painter for our canvas
- QPainter painter(&canvas);
- painter.setRenderHint(QPainter::SmoothPixmapTransform);
-
- // Draw the icon onto our canvas
- if (!icon.isNull())
- {
- painter.drawPixmap(target_pos.x(), target_pos.y(), icon_size.width(), icon_size.height(), icon);
- }
-
- // Draw config icons if necessary
- if (!m_is_list_layout && (paint_config_icon || paint_pad_config_icon))
- {
- const int width = canvas_size.width() * 0.2;
- const QPoint origin = QPoint(canvas_size.width() - width, 0);
- QString icon_path;
-
- if (paint_config_icon && paint_pad_config_icon)
- {
- icon_path = ":/Icons/combo_config_bordered.png";
- }
- else if (paint_config_icon)
- {
- icon_path = ":/Icons/custom_config.png";
- }
- else if (paint_pad_config_icon)
- {
- icon_path = ":/Icons/controllers.png";
- }
-
- QPixmap custom_config_icon(icon_path);
- custom_config_icon.setDevicePixelRatio(device_pixel_ratio);
- painter.drawPixmap(origin, custom_config_icon.scaled(QSize(width, width) * device_pixel_ratio, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation));
- }
-
- // Draw game compatibility icons if necessary
- if (compatibility_color.isValid())
- {
- const int size = canvas_size.height() * 0.2;
- const int spacing = canvas_size.height() * 0.05;
- QColor copyColor = QColor(compatibility_color);
- copyColor.setAlpha(215); // ~85% opacity
- painter.setRenderHint(QPainter::Antialiasing);
- painter.setBrush(QBrush(copyColor));
- painter.setPen(QPen(Qt::black, std::max(canvas_size.width() / 320, canvas_size.height() / 176)));
- painter.drawEllipse(spacing, spacing, size, size);
- }
-
- // Finish the painting
- painter.end();
-
- // Scale and return our final image
- return canvas.scaled(m_icon_size * device_pixel_ratio, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation);
-}
-
-void game_list_frame::SetCustomConfigIcon(QTableWidgetItem* title_item, const game_info& game)
-{
- if (!title_item || !game)
- return;
-
- static QIcon icon_combo_config_bordered(":/Icons/combo_config_bordered.png");
- static QIcon icon_custom_config(":/Icons/custom_config.png");
- static QIcon icon_controllers(":/Icons/controllers.png");
-
- if (game->hasCustomConfig && game->hasCustomPadConfig)
- {
- title_item->setIcon(icon_combo_config_bordered);
- }
- else if (game->hasCustomConfig)
- {
- title_item->setIcon(icon_custom_config);
- }
- else if (game->hasCustomPadConfig)
- {
- title_item->setIcon(icon_controllers);
- }
- else if (!title_item->icon().isNull())
- {
- title_item->setIcon({});
- }
-}
-
void game_list_frame::ShowCustomConfigIcon(const game_info& game)
{
if (!game)
@@ -2416,15 +2164,7 @@ void game_list_frame::ShowCustomConfigIcon(const game_info& game)
}
}
- const QString q_serial = qstr(game->info.serial);
-
- for (int row = 0; row < m_game_list->rowCount(); ++row)
- {
- if (const auto item = m_game_list->item(row, gui::column_serial); item && item->text() == q_serial)
- {
- SetCustomConfigIcon(m_game_list->item(row, gui::column_name), game);
- }
- }
+ m_game_list->set_custom_config_icon(game);
RepaintIcons();
}
@@ -2457,52 +2197,12 @@ void game_list_frame::RepaintIcons(const bool& from_settings)
if (m_is_list_layout)
{
- QPixmap placeholder(m_icon_size);
- placeholder.fill(Qt::transparent);
-
- for (game_info& game : m_game_data)
- {
- game->pxmap = placeholder;
-
- if (movie_item* item = game->item)
- {
- item->set_icon_load_func([this, game, cancel = item->icon_loading_aborted()](int)
- {
- IconLoadFunction(game, cancel);
- });
- item->call_icon_func();
- }
- }
-
- // Fixate vertical header and row height
- m_game_list->verticalHeader()->setMinimumSectionSize(m_icon_size.height());
- m_game_list->verticalHeader()->setMaximumSectionSize(m_icon_size.height());
-
- // Resize the icon column
- m_game_list->resizeColumnToContents(gui::column_icon);
-
- // Shorten the last section to remove horizontal scrollbar if possible
- m_game_list->resizeColumnToContents(gui::column_count - 1);
+ m_game_list->repaint_icons(m_game_data, m_icon_color, m_icon_size, devicePixelRatioF());
}
else
{
- // The game grid needs to be recreated from scratch
- int games_per_row = 0;
-
- if (m_icon_size.width() > 0 && m_icon_size.height() > 0)
- {
- games_per_row = width() / (m_icon_size.width() + m_icon_size.width() * m_game_grid->getMarginFactor() * 2);
- }
-
- const int scroll_position = m_game_grid->verticalScrollBar()->value();
- PopulateGameGrid(games_per_row, m_icon_size, m_icon_color);
- connect(m_game_grid, &QTableWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu);
- connect(m_game_grid, &QTableWidget::itemSelectionChanged, this, &game_list_frame::ItemSelectionChangedSlot);
- connect(m_game_grid, &QTableWidget::itemDoubleClicked, this, &game_list_frame::doubleClickedSlot);
- connect(m_game_grid, &game_list::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar);
- m_central_widget->addWidget(m_game_grid);
- m_central_widget->setCurrentWidget(m_game_grid);
- m_game_grid->verticalScrollBar()->setValue(scroll_position);
+ m_game_grid->set_draw_compat_status_to_grid(m_draw_compat_status_to_grid);
+ m_game_grid->repaint_icons(m_game_data, m_icon_color, m_icon_size, devicePixelRatioF());
}
}
@@ -2520,7 +2220,14 @@ void game_list_frame::SetListMode(const bool& is_list)
Refresh();
- m_central_widget->setCurrentWidget(m_is_list_layout ? m_game_list : m_game_grid);
+ if (m_is_list_layout)
+ {
+ m_central_widget->setCurrentWidget(m_game_list);
+ }
+ else
+ {
+ m_central_widget->setCurrentWidget(m_game_grid);
+ }
}
void game_list_frame::SetSearchText(const QString& text)
@@ -2533,21 +2240,17 @@ void game_list_frame::FocusAndSelectFirstEntryIfNoneIs()
{
if (m_is_list_layout)
{
- if (!m_game_list)
+ if (m_game_list)
{
- return;
+ m_game_list->FocusAndSelectFirstEntryIfNoneIs();
}
-
- m_game_list->FocusAndSelectFirstEntryIfNoneIs();
}
else
{
- if (!m_game_grid)
+ if (m_game_grid)
{
- return;
+ m_game_grid->FocusAndSelectFirstEntryIfNoneIs();
}
-
- m_game_grid->FocusAndSelectFirstEntryIfNoneIs();
}
}
@@ -2557,19 +2260,10 @@ void game_list_frame::closeEvent(QCloseEvent *event)
Q_EMIT GameListFrameClosed();
}
-void game_list_frame::resizeEvent(QResizeEvent *event)
-{
- if (!m_is_list_layout)
- {
- Refresh(false, m_game_grid->selectedItems().count());
- }
- QDockWidget::resizeEvent(event);
-}
-
bool game_list_frame::eventFilter(QObject *object, QEvent *event)
{
// Zoom gamelist/gamegrid
- if (event->type() == QEvent::Wheel && (object == m_game_list->verticalScrollBar() || object == m_game_grid->verticalScrollBar()))
+ if (event->type() == QEvent::Wheel && (object == m_game_list->verticalScrollBar() || object == m_game_grid->scroll_area()->verticalScrollBar()))
{
QWheelEvent *wheel_event = static_cast(event);
@@ -2602,17 +2296,21 @@ bool game_list_frame::eventFilter(QObject *object, QEvent *event)
{
if (key_event->key() == Qt::Key_Enter || key_event->key() == Qt::Key_Return)
{
- QTableWidgetItem* item;
+ game_info gameinfo{};
if (object == m_game_list)
- item = m_game_list->item(m_game_list->currentRow(), gui::column_icon);
- else
- item = m_game_grid->currentItem();
+ {
+ QTableWidgetItem* item = m_game_list->item(m_game_list->currentRow(), gui::column_icon);
- if (!item || !item->isSelected())
- return false;
+ if (!item || !item->isSelected())
+ return false;
- const game_info gameinfo = GetGameInfoFromItem(item);
+ gameinfo = GetGameInfoFromItem(item);
+ }
+ else if (game_list_grid_item* item = static_cast(m_game_grid->selected_item()))
+ {
+ gameinfo = item->game();
+ }
if (!gameinfo)
return false;
@@ -2628,325 +2326,6 @@ bool game_list_frame::eventFilter(QObject *object, QEvent *event)
return QDockWidget::eventFilter(object, event);
}
-/**
- Cleans and readds entries to table widget in UI.
-*/
-void game_list_frame::PopulateGameList()
-{
- int selected_row = -1;
-
- const std::string selected_item = CurrentSelectionPath();
-
- // Release old data
- for (const auto& game : m_game_data)
- {
- game->item = nullptr;
- }
-
- m_game_grid->clear_list();
- m_game_list->clear_list();
-
- m_game_list->setRowCount(m_game_data.size());
-
- // Default locale. Uses current Qt application language.
- const QLocale locale{};
- const Localized localized;
-
- const QString game_icon_path = m_play_hover_movies ? qstr(fs::get_config_dir() + "/Icons/game_icons/") : "";
-
- int row = 0;
- int index = -1;
-
- // Fallback is not needed when at least one entry is visible
- const bool use_search_fallback = std::none_of(m_game_data.begin(), m_game_data.end(), [this](auto& game){ return IsEntryVisible(game); });
-
- for (const auto& game : m_game_data)
- {
- index++;
-
- if (!IsEntryVisible(game, use_search_fallback))
- {
- continue;
- }
-
- const QString serial = qstr(game->info.serial);
- const QString title = m_titles.value(serial, qstr(game->info.name));
- const QString notes = m_notes.value(serial);
-
- // Icon
- custom_table_widget_item* icon_item = new custom_table_widget_item;
- game->item = icon_item;
-
- icon_item->set_icon_func([this, icon_item, game](int)
- {
- if (!icon_item || !game)
- {
- return;
- }
-
- if (std::shared_ptr movie = icon_item->movie(); movie && icon_item->get_active())
- {
- icon_item->setData(Qt::DecorationRole, movie->currentPixmap().scaled(m_icon_size, Qt::KeepAspectRatio));
- }
- else
- {
- std::lock_guard lock(icon_item->pixmap_mutex);
-
- icon_item->setData(Qt::DecorationRole, game->pxmap);
-
- if (!game->has_hover_gif)
- {
- game->pxmap = {};
- }
-
- if (movie)
- {
- movie->stop();
- }
- }
- });
-
- icon_item->set_size_calc_func([this, game, cancel = icon_item->size_on_disk_loading_aborted(), dev_flash = g_cfg_vfs.get_dev_flash()]()
- {
- if (game && game->info.size_on_disk == umax && (!cancel || !cancel->load()))
- {
- if (game->info.path.starts_with(dev_flash))
- {
- // Do not report size of apps inside /dev_flash (it does not make sense to do so)
- game->info.size_on_disk = 0;
- }
- else
- {
- game->info.size_on_disk = fs::get_dir_size(game->info.path, 1, cancel.get());
- }
-
- if (!cancel || !cancel->load())
- {
- Q_EMIT SizeOnDiskReady(game);
- return;
- }
- }
- });
-
- if (m_play_hover_movies && game->has_hover_gif)
- {
- icon_item->init_movie(game_icon_path % serial % "/hover.gif");
- }
-
- icon_item->setData(Qt::UserRole, index, true);
- icon_item->setData(gui::custom_roles::game_role, QVariant::fromValue(game));
-
- // Title
- custom_table_widget_item* title_item = new custom_table_widget_item(title);
- SetCustomConfigIcon(title_item, game);
-
- // Serial
- custom_table_widget_item* serial_item = new custom_table_widget_item(game->info.serial);
-
- if (!notes.isEmpty())
- {
- const QString tool_tip = tr("%0 [%1]\n\nNotes:\n%2").arg(title).arg(serial).arg(notes);
- title_item->setToolTip(tool_tip);
- serial_item->setToolTip(tool_tip);
- }
-
- // Move Support (http://www.psdevwiki.com/ps3/PARAM.SFO#ATTRIBUTE)
- const bool supports_move = game->info.attr & 0x800000;
-
- // Compatibility
- custom_table_widget_item* compat_item = new custom_table_widget_item;
- compat_item->setText(game->compat.text % (game->compat.date.isEmpty() ? QStringLiteral("") : " (" % game->compat.date % ")"));
- compat_item->setData(Qt::UserRole, game->compat.index, true);
- compat_item->setToolTip(game->compat.tooltip);
- if (!game->compat.color.isEmpty())
- {
- compat_item->setData(Qt::DecorationRole, gui::utils::circle_pixmap(game->compat.color, devicePixelRatioF() * 2));
- }
-
- // Version
- QString app_version = qstr(GetGameVersion(game));
-
- if (game->info.bootable && !game->compat.latest_version.isEmpty())
- {
- f64 top_ver = 0.0, app_ver = 0.0;
- const bool unknown = app_version == localized.category.unknown;
- const bool ok_app = !unknown && try_to_float(&app_ver, sstr(app_version), ::std::numeric_limits::min(), ::std::numeric_limits::max());
- const bool ok_top = !unknown && try_to_float(&top_ver, sstr(game->compat.latest_version), ::std::numeric_limits::min(), ::std::numeric_limits::max());
-
- // If the app is bootable and the compat database contains info about the latest patch version:
- // add a hint for available software updates if the app version is unknown or lower than the latest version.
- if (unknown || (ok_top && ok_app && top_ver > app_ver))
- {
- app_version = tr("%0 (Update available: %1)").arg(app_version, game->compat.latest_version);
- }
- }
-
- // Playtimes
- const quint64 elapsed_ms = m_persistent_settings->GetPlaytime(serial);
-
- // Last played (support outdated values)
- QDateTime last_played;
- const QString last_played_str = GetLastPlayedBySerial(serial);
-
- if (!last_played_str.isEmpty())
- {
- last_played = QDateTime::fromString(last_played_str, gui::persistent::last_played_date_format);
-
- if (!last_played.isValid())
- {
- last_played = QDateTime::fromString(last_played_str, gui::persistent::last_played_date_format_old);
- }
- }
-
- const u64 game_size = game->info.size_on_disk;
-
- m_game_list->setItem(row, gui::column_icon, icon_item);
- m_game_list->setItem(row, gui::column_name, title_item);
- m_game_list->setItem(row, gui::column_serial, serial_item);
- m_game_list->setItem(row, gui::column_firmware, new custom_table_widget_item(game->info.fw));
- m_game_list->setItem(row, gui::column_version, new custom_table_widget_item(app_version));
- m_game_list->setItem(row, gui::column_category, new custom_table_widget_item(game->localized_category));
- m_game_list->setItem(row, gui::column_path, new custom_table_widget_item(game->info.path));
- m_game_list->setItem(row, gui::column_move, new custom_table_widget_item(sstr(supports_move ? tr("Supported") : tr("Not Supported")), Qt::UserRole, !supports_move));
- m_game_list->setItem(row, gui::column_resolution, new custom_table_widget_item(GetStringFromU32(game->info.resolution, localized.resolution.mode, true)));
- m_game_list->setItem(row, gui::column_sound, new custom_table_widget_item(GetStringFromU32(game->info.sound_format, localized.sound.format, true)));
- m_game_list->setItem(row, gui::column_parental, new custom_table_widget_item(GetStringFromU32(game->info.parental_lvl, localized.parental.level), Qt::UserRole, game->info.parental_lvl));
- m_game_list->setItem(row, gui::column_last_play, new custom_table_widget_item(locale.toString(last_played, last_played >= QDateTime::currentDateTime().addDays(-7) ? gui::persistent::last_played_date_with_time_of_day_format : gui::persistent::last_played_date_format_new), Qt::UserRole, last_played));
- m_game_list->setItem(row, gui::column_playtime, new custom_table_widget_item(elapsed_ms == 0 ? tr("Never played") : localized.GetVerboseTimeByMs(elapsed_ms), Qt::UserRole, elapsed_ms));
- m_game_list->setItem(row, gui::column_compat, compat_item);
- m_game_list->setItem(row, gui::column_dir_size, new custom_table_widget_item(game_size != umax ? gui::utils::format_byte_size(game_size) : tr("Unknown"), Qt::UserRole, QVariant::fromValue(game_size)));
-
- if (selected_item == game->info.path + game->info.icon_path)
- {
- selected_row = row;
- }
-
- row++;
- }
-
- m_game_list->setRowCount(row);
- m_game_list->selectRow(selected_row);
-}
-
-void game_list_frame::PopulateGameGrid(int maxCols, const QSize& image_size, const QColor& image_color)
-{
- int r = 0;
- int c = 0;
-
- const std::string selected_item = CurrentSelectionPath();
-
- // Release old data
- for (const auto& game : m_game_data)
- {
- game->item = nullptr;
- }
-
- m_game_list->clear_list();
- m_game_grid->deleteLater();
-
- const bool show_text = m_icon_size_index > gui::gl_max_slider_pos * 2 / 5;
-
- if (m_icon_size_index < gui::gl_max_slider_pos * 2 / 3)
- {
- m_game_grid = new game_list_grid(image_size, image_color, m_margin_factor, m_text_factor * 2, show_text);
- }
- else
- {
- m_game_grid = new game_list_grid(image_size, image_color, m_margin_factor, m_text_factor, show_text);
- }
-
- // Get list of matching apps
- std::vector matching_apps;
-
- for (const auto& app : m_game_data)
- {
- if (IsEntryVisible(app))
- {
- matching_apps.push_back(app);
- }
- }
-
- // Fallback is not needed when at least one entry is visible
- if (matching_apps.empty())
- {
- for (const auto& app : m_game_data)
- {
- if (IsEntryVisible(app, true))
- {
- matching_apps.push_back(app);
- }
- }
- }
-
- const int entries = static_cast(matching_apps.size());
-
- // Edge cases!
- if (entries == 0)
- { // For whatever reason, 0%x is division by zero. Absolute nonsense by definition of modulus. But, I'll acquiesce.
- return;
- }
-
- maxCols = std::clamp(maxCols, 1, entries);
-
- const int needs_extra_row = (entries % maxCols) != 0;
- const int max_rows = needs_extra_row + entries / maxCols;
- m_game_grid->setRowCount(max_rows);
- m_game_grid->setColumnCount(maxCols);
-
- const QString game_icon_path = m_play_hover_movies ? qstr(fs::get_config_dir() + "/Icons/game_icons/") : "";
-
- for (const game_info& app : matching_apps)
- {
- const QString serial = qstr(app->info.serial);
- const QString title = m_titles.value(serial, qstr(app->info.name));
- const QString notes = m_notes.value(serial);
-
- movie_item* item = m_game_grid->addItem(app, title, (m_play_hover_movies && app->has_hover_gif) ? (game_icon_path % serial % "/hover.gif") : QStringLiteral(""), r, c);
- ensure(item);
- app->item = item;
- item->setData(gui::game_role, QVariant::fromValue(app));
- item->set_icon_load_func([this, app, cancel = item->icon_loading_aborted()](int)
- {
- IconLoadFunction(app, cancel);
- });
-
- if (!notes.isEmpty())
- {
- item->setToolTip(tr("%0 [%1]\n\nNotes:\n%2").arg(title).arg(serial).arg(notes));
- }
- else
- {
- item->setToolTip(tr("%0 [%1]").arg(title).arg(serial));
- }
-
- if (selected_item == app->info.path + app->info.icon_path)
- {
- m_game_grid->setCurrentItem(item);
- }
-
- if (++c >= maxCols)
- {
- c = 0;
- r++;
- }
- }
-
- if (c != 0)
- { // if left over games exist -- if empty entries exist
- for (int col = c; col < maxCols; ++col)
- {
- movie_item* empty_item = new movie_item();
- empty_item->setFlags(Qt::NoItemFlags);
- m_game_grid->setItem(r, col, empty_item);
- }
- }
-
- m_game_grid->resizeColumnsToContents();
- m_game_grid->resizeRowsToContents();
- m_game_grid->installEventFilter(this);
- m_game_grid->verticalScrollBar()->installEventFilter(this);
-}
-
/**
* Returns false if the game should be hidden because it doesn't match search term in toolbar.
*/
@@ -2995,32 +2374,32 @@ std::string game_list_frame::CurrentSelectionPath()
{
std::string selection;
- QTableWidgetItem* item = nullptr;
+ game_info game{};
if (m_old_layout_is_list)
{
if (!m_game_list->selectedItems().isEmpty())
{
- item = m_game_list->item(m_game_list->currentRow(), 0);
+ if (QTableWidgetItem* item = m_game_list->item(m_game_list->currentRow(), 0))
+ {
+ if (const QVariant var = item->data(gui::game_role); var.canConvert())
+ {
+ game = var.value();
+ }
+ }
}
}
else if (m_game_grid)
{
- if (!m_game_grid->selectedItems().isEmpty())
+ if (game_list_grid_item* item = static_cast(m_game_grid->selected_item()))
{
- item = m_game_grid->currentItem();
+ game = item->game();
}
}
- if (item)
+ if (game)
{
- if (const QVariant var = item->data(gui::game_role); var.canConvert())
- {
- if (const game_info game = var.value())
- {
- selection = game->info.path + game->info.icon_path;
- }
- }
+ selection = game->info.path + game->info.icon_path;
}
m_old_layout_is_list = m_is_list_layout;
@@ -3028,36 +2407,6 @@ std::string game_list_frame::CurrentSelectionPath()
return selection;
}
-std::string game_list_frame::GetStringFromU32(const u32& key, const std::map& map, bool combined)
-{
- QStringList string;
-
- if (combined)
- {
- for (const auto& item : map)
- {
- if (key & item.first)
- {
- string << item.second;
- }
- }
- }
- else
- {
- if (map.find(key) != map.end())
- {
- string << ::at32(map, key);
- }
- }
-
- if (string.isEmpty())
- {
- string << tr("Unknown");
- }
-
- return sstr(string.join(", "));
-}
-
game_info game_list_frame::GetGameInfoByMode(const QTableWidgetItem* item) const
{
if (!item)
@@ -3089,15 +2438,6 @@ game_info game_list_frame::GetGameInfoFromItem(const QTableWidgetItem* item)
return var.value();
}
-QColor game_list_frame::getGridCompatibilityColor(const QString& string) const
-{
- if (m_draw_compat_status_to_grid && !m_is_list_layout)
- {
- return QColor(string);
- }
- return QColor();
-}
-
void game_list_frame::SetShowCompatibilityInGrid(bool show)
{
m_draw_compat_status_to_grid = show;
@@ -3130,61 +2470,6 @@ const QList& game_list_frame::GetGameInfo() const
return m_game_data;
}
-std::string game_list_frame::GetGameVersion(const game_info& game)
-{
- if (game->info.app_ver == sstr(Localized().category.unknown))
- {
- // Fall back to Disc/Pkg Revision
- return game->info.version;
- }
-
- return game->info.app_ver;
-}
-
-void game_list_frame::IconLoadFunction(game_info game, std::shared_ptr> cancel)
-{
- if (cancel && cancel->load())
- {
- return;
- }
-
- static std::unordered_set warn_once_list;
- static shared_mutex s_mtx;
-
- if (game->icon.isNull() && (game->info.icon_path.empty() || !game->icon.load(qstr(game->info.icon_path))))
- {
- if (game_list_log.warning)
- {
- bool logged = false;
- {
- std::lock_guard lock(s_mtx);
- logged = !warn_once_list.emplace(game->info.icon_path).second;
- }
-
- if (!logged)
- {
- game_list_log.warning("Could not load image from path %s", sstr(QDir(qstr(game->info.icon_path)).absolutePath()));
- }
- }
- }
-
- if (!game->item || (cancel && cancel->load()))
- {
- return;
- }
-
- const QColor color = getGridCompatibilityColor(game->compat.color);
- {
- std::lock_guard lock(game->item->pixmap_mutex);
- game->pxmap = PaintedPixmap(game->icon, game->hasCustomConfig, game->hasCustomPadConfig, color);
- }
-
- if (!cancel || !cancel->load())
- {
- Q_EMIT IconReady(game);
- }
-}
-
void game_list_frame::WaitAndAbortRepaintThreads()
{
for (const game_info& game : m_game_data)
diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h
index 0a166fc608..50a18282e5 100644
--- a/rpcs3/rpcs3qt/game_list_frame.h
+++ b/rpcs3/rpcs3qt/game_list_frame.h
@@ -19,6 +19,7 @@
#include
#include
+class game_list_table;
class game_list_grid;
class gui_settings;
class emu_settings;
@@ -33,12 +34,6 @@ public:
explicit game_list_frame(std::shared_ptr gui_settings, std::shared_ptr emu_settings, std::shared_ptr persistent_settings, QWidget* parent = nullptr);
~game_list_frame();
- /** Fix columns with width smaller than the minimal section size */
- void FixNarrowColumns() const;
-
- /** Resizes the columns to their contents and adds a small spacing */
- void ResizeColumnsToContents(int spacing = 20) const;
-
/** Refresh the gamelist with/without loading game data from files. Public so that main frame can refresh after vfs or install */
void Refresh(const bool from_drive = false, const bool scroll_after = true);
@@ -63,11 +58,10 @@ public:
const QList& GetGameInfo() const;
- // Returns the visible version string in the game list
- static std::string GetGameVersion(const game_info& game);
-
void CreateShortcuts(const game_info& gameinfo, const std::set& locations);
+ bool IsEntryVisible(const game_info& game, bool search_fallback = false) const;
+
public Q_SLOTS:
void BatchCreatePPUCaches();
void BatchRemovePPUCaches();
@@ -89,6 +83,7 @@ private Q_SLOTS:
void OnColClicked(int col);
void ShowContextMenu(const QPoint &pos);
void doubleClickedSlot(QTableWidgetItem *item);
+ void doubleClickedSlot(const game_info& game);
void ItemSelectionChangedSlot();
Q_SIGNALS:
void GameListFrameClosed();
@@ -96,28 +91,15 @@ Q_SIGNALS:
void RequestBoot(const game_info& game, cfg_mode config_mode = cfg_mode::custom, const std::string& config_path = "", const std::string& savestate = "");
void RequestIconSizeChange(const int& val);
void NotifyEmuSettingsChange();
- void IconReady(const game_info& game);
- void SizeOnDiskReady(const game_info& game);
void FocusToSearchBar();
protected:
/** Override inherited method from Qt to allow signalling when close happened.*/
void closeEvent(QCloseEvent* event) override;
- void resizeEvent(QResizeEvent *event) override;
bool eventFilter(QObject *object, QEvent *event) override;
private:
void push_path(const std::string& path, std::vector& legit_paths);
- QPixmap PaintedPixmap(const QPixmap& icon, bool paint_config_icon = false, bool paint_pad_config_icon = false, const QColor& color = QColor()) const;
- QColor getGridCompatibilityColor(const QString& string) const;
- void IconLoadFunction(game_info game, std::shared_ptr> cancel);
-
- /** Sets the custom config icon. Only call this for list title items. */
- void SetCustomConfigIcon(QTableWidgetItem* title_item, const game_info& game);
void ShowCustomConfigIcon(const game_info& game);
- void PopulateGameList();
- void PopulateGameGrid(int maxCols, const QSize& image_size, const QColor& image_color);
- bool IsEntryVisible(const game_info& game, bool search_fallback = false);
- void SortGameList();
bool SearchMatchesApp(const QString& name, const QString& serial, bool fallback = false) const;
bool RemoveCustomConfiguration(const std::string& title_id, const game_info& game = nullptr, bool is_interactive = false);
@@ -128,11 +110,9 @@ private:
static bool CreatePPUCache(const std::string& path, const std::string& serial = {});
static bool CreatePPUCache(const game_info& game);
- QString GetLastPlayedBySerial(const QString& serial) const;
static std::string GetCacheDirBySerial(const std::string& serial);
static std::string GetDataDirBySerial(const std::string& serial);
std::string CurrentSelectionPath();
- static std::string GetStringFromU32(const u32& key, const std::map& map, bool combined = false);
game_info GetGameInfoByMode(const QTableWidgetItem* item) const;
static game_info GetGameInfoFromItem(const QTableWidgetItem* item);
@@ -148,13 +128,13 @@ private:
game_list_grid* m_game_grid = nullptr;
// Game List
- game_list* m_game_list = nullptr;
+ game_list_table* m_game_list = nullptr;
game_compatibility* m_game_compat = nullptr;
progress_dialog* m_progress_dialog = nullptr;
QTimer* m_progress_dialog_timer = nullptr;
QList m_columnActs;
- Qt::SortOrder m_col_sort_order;
- int m_sort_column;
+ Qt::SortOrder m_col_sort_order{};
+ int m_sort_column{};
bool m_initial_refresh_done = false;
QMap m_notes;
QMap m_titles;
diff --git a/rpcs3/rpcs3qt/game_list_grid.cpp b/rpcs3/rpcs3qt/game_list_grid.cpp
index daa1e56ea8..23e39fc1ec 100644
--- a/rpcs3/rpcs3qt/game_list_grid.cpp
+++ b/rpcs3/rpcs3qt/game_list_grid.cpp
@@ -1,154 +1,212 @@
+#include "stdafx.h"
#include "game_list_grid.h"
-#include "game_list_grid_delegate.h"
+#include "game_list_grid_item.h"
#include "movie_item.h"
+#include "gui_settings.h"
#include "qt_utils.h"
+#include "Utilities/File.h"
-#include
-#include
+#include
+#include
-game_list_grid::game_list_grid(const QSize& icon_size, QColor icon_color, const qreal& margin_factor, const qreal& text_factor, const bool& showText)
- : game_list()
- , m_icon_size(icon_size)
- , m_icon_color(std::move(icon_color))
- , m_margin_factor(margin_factor)
- , m_text_factor(text_factor)
- , m_text_enabled(showText)
+game_list_grid::game_list_grid()
+ : flow_widget(nullptr), game_list_base()
{
- setObjectName("game_grid");
-
- QSize item_size;
- if (m_text_enabled)
- {
- item_size = m_icon_size + QSize(m_icon_size.width() * m_margin_factor * 2, m_icon_size.height() * m_margin_factor * (m_text_factor + 1));
- }
- else
- {
- item_size = m_icon_size + m_icon_size * m_margin_factor * 2;
- }
-
- grid_item_delegate = new game_list_grid_delegate(item_size, m_margin_factor, m_text_factor, this);
- setItemDelegate(grid_item_delegate);
- setEditTriggers(QAbstractItemView::NoEditTriggers);
- setSelectionBehavior(QAbstractItemView::SelectItems);
- setSelectionMode(QAbstractItemView::SingleSelection);
- setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
- setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
- verticalScrollBar()->setSingleStep(20);
- horizontalScrollBar()->setSingleStep(20);
+ setObjectName("game_list_grid");
setContextMenuPolicy(Qt::CustomContextMenu);
- verticalHeader()->setVisible(false);
- horizontalHeader()->setVisible(false);
- setShowGrid(false);
- setMouseTracking(true);
-}
-void game_list_grid::enableText(const bool& enabled)
-{
- m_text_enabled = enabled;
-}
-
-void game_list_grid::setIconSize(const QSize& size) const
-{
- if (m_text_enabled)
+ m_icon_ready_callback = [this](const game_info& game)
{
- grid_item_delegate->setItemSize(size + QSize(size.width() * m_margin_factor * 2, size.height() * m_margin_factor * (m_text_factor + 1)));
- }
- else
+ Q_EMIT IconReady(game);
+ };
+
+ connect(this, &game_list_grid::IconReady, this, [this](const game_info& game)
{
- grid_item_delegate->setItemSize(size + size * m_margin_factor * 2);
- }
-}
+ if (!game || !game->item) return;
+ game->item->call_icon_func();
+ }, Qt::QueuedConnection); // The default 'AutoConnection' doesn't seem to work in this specific case...
-movie_item* game_list_grid::addItem(const game_info& app, const QString& name, const QString& movie_path, const int& row, const int& col)
-{
- // create item with expanded image, title and position
- movie_item* item = new movie_item;
-
- item->set_icon_func([this, app, item](int)
+ connect(this, &flow_widget::ItemSelectionChanged, this, [this](int index)
{
- if (!item)
+ if (game_list_grid_item* item = static_cast(::at32(items(), index)))
{
- return;
+ Q_EMIT ItemSelectionChanged(item->game());
}
-
- const qreal device_pixel_ratio = devicePixelRatioF();
-
- // define size of expanded image, which is raw image size + margins
- QSizeF exp_size_f;
- if (m_text_enabled)
- {
- exp_size_f = m_icon_size + QSizeF(m_icon_size.width() * m_margin_factor * 2, m_icon_size.height() * m_margin_factor * (m_text_factor + 1));
- }
- else
- {
- exp_size_f = m_icon_size + m_icon_size * m_margin_factor * 2;
- }
-
- std::shared_ptr movie = item->movie();
- const bool draw_movie_frame = movie && movie->isValid() && item->get_active();
- const QSize exp_size = (exp_size_f * device_pixel_ratio).toSize();
-
- // create empty canvas for expanded image
- QImage exp_img(exp_size, QImage::Format_ARGB32);
- exp_img.setDevicePixelRatio(device_pixel_ratio);
- exp_img.fill(Qt::transparent);
-
- // define offset for raw image placement
- QPoint offset(m_icon_size.width() * m_margin_factor, m_icon_size.height() * m_margin_factor);
-
- // place raw image inside expanded image
- QPainter painter(&exp_img);
- painter.setRenderHint(QPainter::SmoothPixmapTransform);
-
- if (draw_movie_frame)
- {
- const QPixmap scaled_movie_frame = movie->currentPixmap().scaled(m_icon_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
- offset += QPoint(m_icon_size.width() / 2 - scaled_movie_frame.width() / 2,
- m_icon_size.height() / 2 - scaled_movie_frame.height() / 2);
- painter.drawPixmap(offset, scaled_movie_frame);
- }
- else
- {
- // create background for image
- QImage bg_img(app->pxmap.size(), QImage::Format_ARGB32);
- bg_img.setDevicePixelRatio(device_pixel_ratio);
- bg_img.fill(m_icon_color);
-
- painter.drawImage(offset, bg_img);
- painter.drawPixmap(offset, app->pxmap);
-
- if (!app->has_hover_gif)
- {
- app->pxmap = {};
- }
-
- if (movie)
- {
- movie->stop();
- }
- }
-
- painter.end();
-
- // create item with expanded image, title and position
- item->setData(Qt::ItemDataRole::DecorationRole, QPixmap::fromImage(exp_img));
});
-
- if (!movie_path.isEmpty())
- {
- item->init_movie(movie_path);
- }
-
- if (m_text_enabled)
- {
- item->setData(Qt::ItemDataRole::DisplayRole, name);
- }
-
- setItem(row, col, item);
- return item;
}
-qreal game_list_grid::getMarginFactor() const
+void game_list_grid::clear_list()
{
- return m_margin_factor;
+ clear();
+}
+
+void game_list_grid::populate(
+ const std::vector& game_data,
+ const QMap& notes_map,
+ const QMap& title_map,
+ const std::string& selected_item_id,
+ bool play_hover_movies)
+{
+ clear_list();
+
+ const QString game_icon_path = play_hover_movies ? QString::fromStdString(fs::get_config_dir() + "/Icons/game_icons/") : "";
+ game_list_grid_item* selected_item = nullptr;
+
+ blockSignals(true);
+
+ for (const auto& game : game_data)
+ {
+ const QString serial = QString::fromStdString(game->info.serial);
+ const QString title = title_map.value(serial, QString::fromStdString(game->info.name)).simplified();
+ const QString notes = notes_map.value(serial);
+
+ game_list_grid_item* item = new game_list_grid_item(this, game, title);
+ item->installEventFilter(this);
+ item->setFocusPolicy(Qt::StrongFocus);
+
+ game->item = item;
+
+ if (notes.isEmpty())
+ {
+ item->setToolTip(tr("%0 [%1]").arg(title).arg(serial));
+ }
+ else
+ {
+ item->setToolTip(tr("%0 [%1]\n\nNotes:\n%2").arg(title).arg(serial).arg(notes));
+ }
+
+ item->set_icon_func([this, item, game](int)
+ {
+ if (!item || !game)
+ {
+ return;
+ }
+
+ if (std::shared_ptr movie = item->movie(); movie && item->get_active())
+ {
+ item->set_icon(gui::utils::get_centered_pixmap(movie->currentPixmap(), m_icon_size, 0, 0, 1.0, Qt::FastTransformation));
+ }
+ else
+ {
+ std::lock_guard lock(item->pixmap_mutex);
+
+ item->set_icon(game->pxmap);
+
+ if (!game->has_hover_gif)
+ {
+ game->pxmap = {};
+ }
+
+ if (movie)
+ {
+ movie->stop();
+ }
+ }
+ });
+
+ if (play_hover_movies && game->has_hover_gif)
+ {
+ item->init_movie(game_icon_path % serial % "/hover.gif");
+ }
+
+ if (selected_item_id == game->info.path + game->info.icon_path)
+ {
+ selected_item = item;
+ }
+
+ add_widget(item);
+ }
+
+ blockSignals(false);
+
+ // Update layout before setting focus on the selected item
+ show();
+
+ QApplication::processEvents();
+
+ select_item(selected_item);
+}
+
+void game_list_grid::repaint_icons(QList& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio)
+{
+ m_icon_size = icon_size;
+ m_icon_color = icon_color;
+
+ QPixmap placeholder(icon_size * device_pixel_ratio);
+ placeholder.setDevicePixelRatio(device_pixel_ratio);
+ placeholder.fill(Qt::transparent);
+
+ const bool show_title = m_icon_size.width() > (gui::gl_icon_size_medium.width() + gui::gl_icon_size_small.width()) / 2;
+
+ for (game_info& game : game_data)
+ {
+ if (game_list_grid_item* item = static_cast(game->item))
+ {
+ if (item->icon_loading())
+ {
+ // We already have an icon. Simply set the icon size to let the label scale itself in a quick and dirty fashion.
+ item->set_icon_size(m_icon_size);
+ }
+ else
+ {
+ // We don't have an icon. Set a placeholder to initialize the layout.
+ game->pxmap = placeholder;
+ item->call_icon_func();
+ }
+
+ item->set_icon_load_func([this, game, device_pixel_ratio, cancel = item->icon_loading_aborted()](int)
+ {
+ IconLoadFunction(game, device_pixel_ratio, cancel);
+ });
+
+ item->adjust_size();
+ item->show_title(show_title);
+ item->got_visible = false;
+ }
+ }
+}
+
+void game_list_grid::FocusAndSelectFirstEntryIfNoneIs()
+{
+ if (items().empty() == false)
+ {
+ items().first()->setFocus();
+ }
+}
+
+bool game_list_grid::eventFilter(QObject* watched, QEvent* event)
+{
+ if (!event)
+ {
+ return false;
+ }
+
+ if (event->type() == QEvent::MouseButtonDblClick && static_cast(event)->button() == Qt::LeftButton)
+ {
+ if (game_list_grid_item* item = static_cast(watched))
+ {
+ Q_EMIT ItemDoubleClicked(item->game());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void game_list_grid::keyPressEvent(QKeyEvent* event)
+{
+ if (!event)
+ {
+ return;
+ }
+
+ const auto modifiers = event->modifiers();
+
+ if (modifiers == Qt::ControlModifier && event->key() == Qt::Key_F && !event->isAutoRepeat())
+ {
+ Q_EMIT FocusToSearchBar();
+ return;
+ }
+
+ flow_widget::keyPressEvent(event);
}
diff --git a/rpcs3/rpcs3qt/game_list_grid.h b/rpcs3/rpcs3qt/game_list_grid.h
index f2c7bd36f9..318d932baf 100644
--- a/rpcs3/rpcs3qt/game_list_grid.h
+++ b/rpcs3/rpcs3qt/game_list_grid.h
@@ -1,28 +1,37 @@
#pragma once
-#include "game_list.h"
+#include "game_list_base.h"
+#include "flow_widget.h"
-class game_list_grid_delegate;
+#include
-class game_list_grid : public game_list
+class game_list_grid : public flow_widget, public game_list_base
{
Q_OBJECT
- QSize m_icon_size;
- QColor m_icon_color;
- qreal m_margin_factor;
- qreal m_text_factor;
- bool m_text_enabled = true;
-
public:
- explicit game_list_grid(const QSize& icon_size, QColor icon_color, const qreal& margin_factor, const qreal& text_factor, const bool& showText);
+ explicit game_list_grid();
- void enableText(const bool& enabled);
- void setIconSize(const QSize& size) const;
- movie_item* addItem(const game_info& app, const QString& name, const QString& movie_path, const int& row, const int& col);
+ void clear_list() override;
- [[nodiscard]] qreal getMarginFactor() const;
+ void populate(
+ const std::vector& game_data,
+ const QMap& notes_map,
+ const QMap& title_map,
+ const std::string& selected_item_id,
+ bool play_hover_movies) override;
-private:
- game_list_grid_delegate* grid_item_delegate;
+ void repaint_icons(QList& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) override;
+
+ bool eventFilter(QObject* watched, QEvent* event) override;
+ void keyPressEvent(QKeyEvent* event) override;
+
+public Q_SLOTS:
+ void FocusAndSelectFirstEntryIfNoneIs();
+
+Q_SIGNALS:
+ void FocusToSearchBar();
+ void ItemDoubleClicked(const game_info& game);
+ void ItemSelectionChanged(const game_info& game);
+ void IconReady(const game_info& game);
};
diff --git a/rpcs3/rpcs3qt/game_list_grid_delegate.cpp b/rpcs3/rpcs3qt/game_list_grid_delegate.cpp
deleted file mode 100644
index 5032ffa8a4..0000000000
--- a/rpcs3/rpcs3qt/game_list_grid_delegate.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-#include "game_list_grid_delegate.h"
-#include "movie_item.h"
-
-#include
-
-game_list_grid_delegate::game_list_grid_delegate(const QSize& size, const qreal& margin_factor, const qreal& text_factor, QObject *parent)
- : QStyledItemDelegate(parent), m_size(size), m_margin_factor(margin_factor), m_text_factor(text_factor)
-{
-}
-
-void game_list_grid_delegate::initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const
-{
- Q_UNUSED(index)
-
- // Remove the focus frame around selected items
- option->state &= ~QStyle::State_HasFocus;
-
- // Call initStyleOption without a model index, since we want to paint the relevant data ourselves
- QStyledItemDelegate::initStyleOption(option, QModelIndex());
-}
-
-void game_list_grid_delegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
-{
- const QRect r = option.rect;
-
- painter->setRenderHints(QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
- painter->eraseRect(r);
-
- // Paint from our stylesheet
- QStyledItemDelegate::paint(painter, option, index);
-
- // Check if the item is visible
- if (const QTableWidget* table = static_cast(parent()))
- {
- if (movie_item* item = static_cast(table->item(index.row(), index.column())))
- {
- if (!table->visibleRegion().intersects(table->visualItemRect(item)))
- {
- // Skip all further actions if the item is not visible
- return;
- }
-
- if (!item->icon_loading())
- {
- item->call_icon_load_func(index.row());
- }
- }
- }
-
- // Get title and image
- const QPixmap image = qvariant_cast(index.data(Qt::DecorationRole));
- const QString title = index.data(Qt::DisplayRole).toString();
-
- // image
- if (image.isNull() == false)
- {
- painter->drawPixmap(option.rect, image);
- }
-
- const int h = r.height() / (1 + m_margin_factor + m_margin_factor * m_text_factor);
- const int height = r.height() - h - h * m_margin_factor;
- const int top = r.bottom() - height;
-
- // title
- if (option.state & QStyle::State_Selected)
- {
- painter->setPen(QPen(option.palette.color(QPalette::HighlightedText), 1, Qt::SolidLine));
- }
- else
- {
- painter->setPen(QPen(option.palette.color(QPalette::WindowText), 1, Qt::SolidLine));
- }
-
- painter->setFont(option.font);
- painter->drawText(QRect(r.left(), top, r.width(), height), +Qt::TextWordWrap | +Qt::AlignCenter, title);
-}
-
-QSize game_list_grid_delegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
-{
- Q_UNUSED(option)
- Q_UNUSED(index)
- return m_size;
-}
-
-void game_list_grid_delegate::setItemSize(const QSize& size)
-{
- m_size = size;
-}
diff --git a/rpcs3/rpcs3qt/game_list_grid_delegate.h b/rpcs3/rpcs3qt/game_list_grid_delegate.h
deleted file mode 100644
index 7ec6ecde76..0000000000
--- a/rpcs3/rpcs3qt/game_list_grid_delegate.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include
-#include
-
-class game_list_grid_delegate : public QStyledItemDelegate
-{
-public:
- game_list_grid_delegate(const QSize& imageSize, const qreal& margin_factor, const qreal& margin_ratio, QObject *parent = nullptr);
-
- void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override;
- void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
- QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const override;
- void setItemSize(const QSize& size);
-private:
- QSize m_size;
- qreal m_margin_factor;
- qreal m_text_factor;
-};
diff --git a/rpcs3/rpcs3qt/game_list_grid_item.cpp b/rpcs3/rpcs3qt/game_list_grid_item.cpp
new file mode 100644
index 0000000000..9b369761e7
--- /dev/null
+++ b/rpcs3/rpcs3qt/game_list_grid_item.cpp
@@ -0,0 +1,86 @@
+#include "game_list_grid_item.h"
+
+#include
+#include
+
+game_list_grid_item::game_list_grid_item(QWidget* parent, game_info game, const QString& title)
+ : flow_widget_item(parent), movie_item_base(), m_game(std::move(game))
+{
+ setObjectName("game_list_grid_item");
+
+ cb_on_first_visibility = [this]()
+ {
+ if (!icon_loading())
+ {
+ call_icon_load_func(0);
+ }
+ };
+
+ m_icon_label = new QLabel(this);
+ m_icon_label->setObjectName("game_list_grid_item_icon_label");
+ m_icon_label->setAttribute(Qt::WA_TranslucentBackground);
+ m_icon_label->setScaledContents(true);
+
+ m_title_label = new QLabel(title, this);
+ m_title_label->setObjectName("game_list_grid_item_title_label");
+ m_title_label->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
+ m_title_label->setWordWrap(true);
+ m_title_label->setVisible(false);
+
+ QVBoxLayout* layout = new QVBoxLayout(this);
+ layout->addWidget(m_icon_label, 1);
+ layout->addWidget(m_title_label, 0);
+
+ setLayout(layout);
+}
+
+void game_list_grid_item::set_icon_size(const QSize& size)
+{
+ m_icon_size = size;
+}
+
+void game_list_grid_item::set_icon(const QPixmap& pixmap)
+{
+ m_icon_size = pixmap.size() / devicePixelRatioF();
+ m_icon_label->setPixmap(pixmap);
+}
+
+void game_list_grid_item::adjust_size()
+{
+ m_icon_label->setMinimumSize(m_icon_size);
+ m_icon_label->setMaximumSize(m_icon_size);
+ m_title_label->setMaximumWidth(m_icon_size.width());
+}
+
+void game_list_grid_item::show_title(bool visible)
+{
+ if (m_title_label)
+ {
+ m_title_label->setVisible(visible);
+ }
+}
+
+void game_list_grid_item::polish_style()
+{
+ flow_widget_item::polish_style();
+
+ m_title_label->style()->unpolish(m_title_label);
+ m_title_label->style()->polish(m_title_label);
+}
+
+bool game_list_grid_item::event(QEvent* event)
+{
+ switch (event->type())
+ {
+ case QEvent::HoverEnter:
+ set_active(true);
+ break;
+ case QEvent::HoverLeave:
+ set_active(false);
+ break;
+ default:
+ break;
+ }
+
+ return flow_widget_item::event(event);
+}
diff --git a/rpcs3/rpcs3qt/game_list_grid_item.h b/rpcs3/rpcs3qt/game_list_grid_item.h
new file mode 100644
index 0000000000..7738548238
--- /dev/null
+++ b/rpcs3/rpcs3qt/game_list_grid_item.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "flow_widget_item.h"
+#include "movie_item_base.h"
+#include "game_list_base.h"
+
+#include
+
+class game_list_grid_item : public flow_widget_item, public movie_item_base
+{
+ Q_OBJECT
+
+public:
+ game_list_grid_item(QWidget* parent, game_info game, const QString& title);
+
+ void set_icon_size(const QSize& size);
+ void set_icon(const QPixmap& pixmap);
+ void adjust_size();
+
+ const game_info& game() const
+ {
+ return m_game;
+ }
+
+ void show_title(bool visible);
+
+ void polish_style() override;
+
+ bool event(QEvent* event) override;
+
+private:
+ QSize m_icon_size{};
+ QLabel* m_icon_label{};
+ QLabel* m_title_label{};
+ game_info m_game{};
+};
diff --git a/rpcs3/rpcs3qt/game_list_table.cpp b/rpcs3/rpcs3qt/game_list_table.cpp
new file mode 100644
index 0000000000..8f64391699
--- /dev/null
+++ b/rpcs3/rpcs3qt/game_list_table.cpp
@@ -0,0 +1,415 @@
+#include "stdafx.h"
+#include "game_list_table.h"
+#include "game_list_delegate.h"
+#include "game_list_frame.h"
+#include "gui_settings.h"
+#include "localized.h"
+#include "custom_table_widget_item.h"
+#include "persistent_settings.h"
+#include "qt_utils.h"
+
+#include "Emu/vfs_config.h"
+#include "Utilities/StrUtil.h"
+
+#include
+#include
+#include
+#include
+
+game_list_table::game_list_table(game_list_frame* frame, std::shared_ptr persistent_settings)
+ : game_list(), m_game_list_frame(frame), m_persistent_settings(std::move(persistent_settings))
+{
+ m_is_list_layout = true;
+
+ setShowGrid(false);
+ setItemDelegate(new game_list_delegate(this));
+ setEditTriggers(QAbstractItemView::NoEditTriggers);
+ setSelectionBehavior(QAbstractItemView::SelectRows);
+ setSelectionMode(QAbstractItemView::SingleSelection);
+ setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
+ setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
+ verticalScrollBar()->installEventFilter(this);
+ verticalScrollBar()->setSingleStep(20);
+ horizontalScrollBar()->setSingleStep(20);
+ verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+ verticalHeader()->setVisible(false);
+ horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
+ horizontalHeader()->setHighlightSections(false);
+ horizontalHeader()->setSortIndicatorShown(true);
+ horizontalHeader()->setStretchLastSection(true);
+ horizontalHeader()->setDefaultSectionSize(150);
+ horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
+ setContextMenuPolicy(Qt::CustomContextMenu);
+ setAlternatingRowColors(true);
+ installEventFilter(this);
+ setColumnCount(gui::column_count);
+ setMouseTracking(true);
+
+ connect(this, &game_list_table::size_on_disk_ready, this, [this](const game_info& game)
+ {
+ if (!game || !game->item) return;
+ if (QTableWidgetItem* size_item = item(static_cast(game->item)->row(), gui::column_dir_size))
+ {
+ const u64& game_size = game->info.size_on_disk;
+ size_item->setText(game_size != umax ? gui::utils::format_byte_size(game_size) : tr("Unknown"));
+ size_item->setData(Qt::UserRole, QVariant::fromValue(game_size));
+ }
+ });
+
+ connect(this, &game_list::IconReady, this, [this](const game_info& game)
+ {
+ if (!game || !game->item) return;
+ game->item->call_icon_func();
+ });
+}
+
+void game_list_table::restore_layout(const QByteArray& state)
+{
+ // Resize to fit and get the ideal icon column width
+ resize_columns_to_contents();
+ const int icon_column_width = columnWidth(gui::column_icon);
+
+ // Restore header layout from last session
+ if (!horizontalHeader()->restoreState(state) && rowCount())
+ {
+ // Nothing to do
+ }
+
+ // Make sure no columns are squished
+ fix_narrow_columns();
+
+ // Make sure that the icon column is large enough for the actual items.
+ // This is important if the list appeared as empty when closing the software before.
+ horizontalHeader()->resizeSection(gui::column_icon, icon_column_width);
+
+ // Save new header state
+ horizontalHeader()->restoreState(horizontalHeader()->saveState());
+}
+
+void game_list_table::fix_narrow_columns()
+{
+ QApplication::processEvents();
+
+ // handle columns (other than the icon column) that have zero width after showing them (stuck between others)
+ for (int col = 1; col < columnCount(); ++col)
+ {
+ if (isColumnHidden(col))
+ {
+ continue;
+ }
+
+ if (columnWidth(col) <= horizontalHeader()->minimumSectionSize())
+ {
+ setColumnWidth(col, horizontalHeader()->minimumSectionSize());
+ }
+ }
+}
+
+void game_list_table::resize_columns_to_contents(int spacing)
+{
+ verticalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents);
+ horizontalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents);
+
+ // Make non-icon columns slighty bigger for better visuals
+ for (int i = 1; i < columnCount(); i++)
+ {
+ if (isColumnHidden(i))
+ {
+ continue;
+ }
+
+ const int size = horizontalHeader()->sectionSize(i) + spacing;
+ horizontalHeader()->resizeSection(i, size);
+ }
+}
+
+void game_list_table::adjust_icon_column()
+{
+ // Fixate vertical header and row height
+ verticalHeader()->setMinimumSectionSize(m_icon_size.height());
+ verticalHeader()->setMaximumSectionSize(m_icon_size.height());
+
+ // Resize the icon column
+ resizeColumnToContents(gui::column_icon);
+
+ // Shorten the last section to remove horizontal scrollbar if possible
+ resizeColumnToContents(gui::column_count - 1);
+}
+
+void game_list_table::sort(int game_count, int sort_column, Qt::SortOrder col_sort_order)
+{
+ // Back-up old header sizes to handle unwanted column resize in case of zero search results
+ const int old_row_count = rowCount();
+ const int old_game_count = game_count;
+
+ std::vector column_widths(columnCount());
+ for (int i = 0; i < columnCount(); i++)
+ {
+ column_widths[i] = columnWidth(i);
+ }
+
+ // Sorting resizes hidden columns, so unhide them as a workaround
+ std::vector columns_to_hide;
+
+ for (int i = 0; i < columnCount(); i++)
+ {
+ if (isColumnHidden(i))
+ {
+ setColumnHidden(i, false);
+ columns_to_hide.push_back(i);
+ }
+ }
+
+ // Sort the list by column and sort order
+ sortByColumn(sort_column, col_sort_order);
+
+ // Hide columns again
+ for (int col : columns_to_hide)
+ {
+ setColumnHidden(col, true);
+ }
+
+ // Don't resize the columns if no game is shown to preserve the header settings
+ if (!rowCount())
+ {
+ for (int i = 0; i < columnCount(); i++)
+ {
+ setColumnWidth(i, column_widths[i]);
+ }
+
+ horizontalHeader()->setSectionResizeMode(gui::column_icon, QHeaderView::Fixed);
+ return;
+ }
+
+ // Fixate vertical header and row height
+ verticalHeader()->setMinimumSectionSize(m_icon_size.height());
+ verticalHeader()->setMaximumSectionSize(m_icon_size.height());
+ resizeRowsToContents();
+
+ // Resize columns if the game list was empty before
+ if (!old_row_count && !old_game_count)
+ {
+ resize_columns_to_contents();
+ }
+ else
+ {
+ resizeColumnToContents(gui::column_icon);
+ }
+
+ // Fixate icon column
+ horizontalHeader()->setSectionResizeMode(gui::column_icon, QHeaderView::Fixed);
+
+ // Shorten the last section to remove horizontal scrollbar if possible
+ resizeColumnToContents(gui::column_count - 1);
+}
+
+void game_list_table::set_custom_config_icon(const game_info& game)
+{
+ if (!game)
+ {
+ return;
+ }
+
+ const QString serial = QString::fromStdString(game->info.serial);
+
+ for (int row = 0; row < rowCount(); ++row)
+ {
+ if (QTableWidgetItem* title_item = item(row, gui::column_name))
+ {
+ if (const QTableWidgetItem* serial_item = item(row, gui::column_serial); serial_item && serial_item->text() == serial)
+ {
+ title_item->setIcon(game_list_base::GetCustomConfigIcon(game));
+ }
+ }
+ }
+}
+
+void game_list_table::populate(
+ const std::vector& game_data,
+ const QMap& notes_map,
+ const QMap& title_map,
+ const std::string& selected_item_id,
+ bool play_hover_movies)
+{
+ clear_list();
+
+ setRowCount(::narrow(game_data.size()));
+
+ // Default locale. Uses current Qt application language.
+ const QLocale locale{};
+ const Localized localized;
+
+ const QString game_icon_path = play_hover_movies ? QString::fromStdString(fs::get_config_dir() + "/Icons/game_icons/") : "";
+ const std::string dev_flash = g_cfg_vfs.get_dev_flash();
+
+ int row = 0;
+ int index = -1;
+ int selected_row = -1;
+
+ for (const auto& game : game_data)
+ {
+ index++;
+
+ const QString serial = QString::fromStdString(game->info.serial);
+ const QString title = title_map.value(serial, QString::fromStdString(game->info.name));
+ const QString notes = notes_map.value(serial);
+
+ // Icon
+ custom_table_widget_item* icon_item = new custom_table_widget_item;
+ game->item = icon_item;
+
+ icon_item->set_icon_func([this, icon_item, game](int)
+ {
+ if (!icon_item || !game)
+ {
+ return;
+ }
+
+ if (std::shared_ptr movie = icon_item->movie(); movie && icon_item->get_active())
+ {
+ icon_item->setData(Qt::DecorationRole, movie->currentPixmap().scaled(m_icon_size, Qt::KeepAspectRatio));
+ }
+ else
+ {
+ std::lock_guard lock(icon_item->pixmap_mutex);
+
+ icon_item->setData(Qt::DecorationRole, game->pxmap);
+
+ if (!game->has_hover_gif)
+ {
+ game->pxmap = {};
+ }
+
+ if (movie)
+ {
+ movie->stop();
+ }
+ }
+ });
+
+ icon_item->set_size_calc_func([this, game, cancel = icon_item->size_on_disk_loading_aborted(), dev_flash]()
+ {
+ if (game && game->info.size_on_disk == umax && (!cancel || !cancel->load()))
+ {
+ if (game->info.path.starts_with(dev_flash))
+ {
+ // Do not report size of apps inside /dev_flash (it does not make sense to do so)
+ game->info.size_on_disk = 0;
+ }
+ else
+ {
+ game->info.size_on_disk = fs::get_dir_size(game->info.path, 1, cancel.get());
+ }
+
+ if (!cancel || !cancel->load())
+ {
+ Q_EMIT size_on_disk_ready(game);
+ return;
+ }
+ }
+ });
+
+ if (play_hover_movies && game->has_hover_gif)
+ {
+ icon_item->init_movie(game_icon_path % serial % "/hover.gif");
+ }
+
+ icon_item->setData(Qt::UserRole, index, true);
+ icon_item->setData(gui::custom_roles::game_role, QVariant::fromValue(game));
+
+ // Title
+ custom_table_widget_item* title_item = new custom_table_widget_item(title);
+ title_item->setIcon(game_list_base::GetCustomConfigIcon(game));
+
+ // Serial
+ custom_table_widget_item* serial_item = new custom_table_widget_item(game->info.serial);
+
+ if (!notes.isEmpty())
+ {
+ const QString tool_tip = tr("%0 [%1]\n\nNotes:\n%2").arg(title).arg(serial).arg(notes);
+ title_item->setToolTip(tool_tip);
+ serial_item->setToolTip(tool_tip);
+ }
+
+ // Move Support (http://www.psdevwiki.com/ps3/PARAM.SFO#ATTRIBUTE)
+ const bool supports_move = game->info.attr & 0x800000;
+
+ // Compatibility
+ custom_table_widget_item* compat_item = new custom_table_widget_item;
+ compat_item->setText(game->compat.text % (game->compat.date.isEmpty() ? QStringLiteral("") : " (" % game->compat.date % ")"));
+ compat_item->setData(Qt::UserRole, game->compat.index, true);
+ compat_item->setToolTip(game->compat.tooltip);
+ if (!game->compat.color.isEmpty())
+ {
+ compat_item->setData(Qt::DecorationRole, gui::utils::circle_pixmap(game->compat.color, devicePixelRatioF() * 2));
+ }
+
+ // Version
+ QString app_version = QString::fromStdString(game_list::GetGameVersion(game));
+
+ if (game->info.bootable && !game->compat.latest_version.isEmpty())
+ {
+ f64 top_ver = 0.0, app_ver = 0.0;
+ const bool unknown = app_version == localized.category.unknown;
+ const bool ok_app = !unknown && try_to_float(&app_ver, app_version.toStdString(), ::std::numeric_limits::min(), ::std::numeric_limits::max());
+ const bool ok_top = !unknown && try_to_float(&top_ver, game->compat.latest_version.toStdString(), ::std::numeric_limits::min(), ::std::numeric_limits::max());
+
+ // If the app is bootable and the compat database contains info about the latest patch version:
+ // add a hint for available software updates if the app version is unknown or lower than the latest version.
+ if (unknown || (ok_top && ok_app && top_ver > app_ver))
+ {
+ app_version = tr("%0 (Update available: %1)").arg(app_version, game->compat.latest_version);
+ }
+ }
+
+ // Playtimes
+ const quint64 elapsed_ms = m_persistent_settings->GetPlaytime(serial);
+
+ // Last played (support outdated values)
+ QDateTime last_played;
+ const QString last_played_str = m_persistent_settings->GetLastPlayed(serial);
+
+ if (!last_played_str.isEmpty())
+ {
+ last_played = QDateTime::fromString(last_played_str, gui::persistent::last_played_date_format);
+
+ if (!last_played.isValid())
+ {
+ last_played = QDateTime::fromString(last_played_str, gui::persistent::last_played_date_format_old);
+ }
+ }
+
+ const u64 game_size = game->info.size_on_disk;
+
+ setItem(row, gui::column_icon, icon_item);
+ setItem(row, gui::column_name, title_item);
+ setItem(row, gui::column_serial, serial_item);
+ setItem(row, gui::column_firmware, new custom_table_widget_item(game->info.fw));
+ setItem(row, gui::column_version, new custom_table_widget_item(app_version));
+ setItem(row, gui::column_category, new custom_table_widget_item(game->localized_category));
+ setItem(row, gui::column_path, new custom_table_widget_item(game->info.path));
+ setItem(row, gui::column_move, new custom_table_widget_item((supports_move ? tr("Supported") : tr("Not Supported")).toStdString(), Qt::UserRole, !supports_move));
+ setItem(row, gui::column_resolution, new custom_table_widget_item(Localized::GetStringFromU32(game->info.resolution, localized.resolution.mode, true)));
+ setItem(row, gui::column_sound, new custom_table_widget_item(Localized::GetStringFromU32(game->info.sound_format, localized.sound.format, true)));
+ setItem(row, gui::column_parental, new custom_table_widget_item(Localized::GetStringFromU32(game->info.parental_lvl, localized.parental.level), Qt::UserRole, game->info.parental_lvl));
+ setItem(row, gui::column_last_play, new custom_table_widget_item(locale.toString(last_played, last_played >= QDateTime::currentDateTime().addDays(-7) ? gui::persistent::last_played_date_with_time_of_day_format : gui::persistent::last_played_date_format_new), Qt::UserRole, last_played));
+ setItem(row, gui::column_playtime, new custom_table_widget_item(elapsed_ms == 0 ? tr("Never played") : localized.GetVerboseTimeByMs(elapsed_ms), Qt::UserRole, elapsed_ms));
+ setItem(row, gui::column_compat, compat_item);
+ setItem(row, gui::column_dir_size, new custom_table_widget_item(game_size != umax ? gui::utils::format_byte_size(game_size) : tr("Unknown"), Qt::UserRole, QVariant::fromValue(game_size)));
+
+ if (selected_item_id == game->info.path + game->info.icon_path)
+ {
+ selected_row = row;
+ }
+
+ row++;
+ }
+
+ selectRow(selected_row);
+}
+
+void game_list_table::repaint_icons(QList& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio)
+{
+ game_list_base::repaint_icons(game_data, icon_color, icon_size, device_pixel_ratio);
+ adjust_icon_column();
+}
diff --git a/rpcs3/rpcs3qt/game_list_table.h b/rpcs3/rpcs3qt/game_list_table.h
new file mode 100644
index 0000000000..e245bd11b7
--- /dev/null
+++ b/rpcs3/rpcs3qt/game_list_table.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include "game_list.h"
+
+class persistent_settings;
+class game_list_frame;
+
+class game_list_table : public game_list
+{
+ Q_OBJECT
+
+public:
+ game_list_table(game_list_frame* frame, std::shared_ptr persistent_settings);
+
+ /** Restores the initial layout of the table */
+ void restore_layout(const QByteArray& state);
+
+ /** Fix columns with width smaller than the minimal section size */
+ void fix_narrow_columns();
+
+ /** Resizes the columns to their contents and adds a small spacing */
+ void resize_columns_to_contents(int spacing = 20);
+
+ void adjust_icon_column();
+
+ void sort(int game_count, int sort_column, Qt::SortOrder col_sort_order);
+
+ void set_custom_config_icon(const game_info& game);
+
+ void populate(
+ const std::vector& game_data,
+ const QMap& notes_map,
+ const QMap& title_map,
+ const std::string& selected_item_id,
+ bool play_hover_movies) override;
+
+ void repaint_icons(QList& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) override;
+
+Q_SIGNALS:
+ void size_on_disk_ready(const game_info& game);
+
+private:
+ game_list_frame* m_game_list_frame{};
+ std::shared_ptr m_persistent_settings;
+};
diff --git a/rpcs3/rpcs3qt/localized.cpp b/rpcs3/rpcs3qt/localized.cpp
index f0f176c5f0..ab6f9c34cd 100644
--- a/rpcs3/rpcs3qt/localized.cpp
+++ b/rpcs3/rpcs3qt/localized.cpp
@@ -47,6 +47,36 @@ QString Localized::GetVerboseTimeByMs(quint64 elapsed_ms, bool show_days) const
return str_seconds;
}
+std::string Localized::GetStringFromU32(const u32& key, const std::map& map, bool combined)
+{
+ QStringList string;
+
+ if (combined)
+ {
+ for (const auto& item : map)
+ {
+ if (key & item.first)
+ {
+ string << item.second;
+ }
+ }
+ }
+ else
+ {
+ if (map.find(key) != map.end())
+ {
+ string << ::at32(map, key);
+ }
+ }
+
+ if (string.isEmpty())
+ {
+ string << tr("Unknown");
+ }
+
+ return string.join(", ").toStdString();
+}
+
Localized::resolution::resolution()
: mode({
{ psf::resolution_flag::_480p, tr("480p") },
diff --git a/rpcs3/rpcs3qt/localized.h b/rpcs3/rpcs3qt/localized.h
index 6245d586cd..cfd3b9e437 100644
--- a/rpcs3/rpcs3qt/localized.h
+++ b/rpcs3/rpcs3qt/localized.h
@@ -17,6 +17,7 @@ public:
Localized() {}
QString GetVerboseTimeByMs(quint64 elapsed_ms, bool show_days = false) const;
+ static std::string GetStringFromU32(const u32& key, const std::map& map, bool combined = false);
const struct category // (see PARAM.SFO in psdevwiki.com) TODO: Disc Categories
{
diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp
index 2e346fa868..f2909f5010 100644
--- a/rpcs3/rpcs3qt/main_window.cpp
+++ b/rpcs3/rpcs3qt/main_window.cpp
@@ -2430,7 +2430,7 @@ void main_window::CreateConnects()
{
if (game)
{
- games[game->info.serial].insert(game_list_frame::GetGameVersion(game));
+ games[game->info.serial].insert(game_list::GetGameVersion(game));
}
}
}
diff --git a/rpcs3/rpcs3qt/memory_string_searcher.cpp b/rpcs3/rpcs3qt/memory_string_searcher.cpp
index 28cf5b6ff9..12bd10c9ed 100644
--- a/rpcs3/rpcs3qt/memory_string_searcher.cpp
+++ b/rpcs3/rpcs3qt/memory_string_searcher.cpp
@@ -8,11 +8,6 @@
#include "Utilities/Thread.h"
#include "Utilities/StrUtil.h"
-#include
-#include
-#include
-#include
-#include
#include
#include
@@ -24,8 +19,6 @@
LOG_CHANNEL(gui_log, "GUI");
-constexpr auto qstr = QString::fromStdString;
-
template <>
void fmt_class_string::format(std::string& out, u64 arg)
{
diff --git a/rpcs3/rpcs3qt/movie_item.cpp b/rpcs3/rpcs3qt/movie_item.cpp
index 7c65b3781e..3cfb269709 100644
--- a/rpcs3/rpcs3qt/movie_item.cpp
+++ b/rpcs3/rpcs3qt/movie_item.cpp
@@ -1,152 +1,14 @@
#include "stdafx.h"
#include "movie_item.h"
-movie_item::movie_item() : QTableWidgetItem()
+movie_item::movie_item() : QTableWidgetItem(), movie_item_base()
{
- init_pointers();
}
-movie_item::movie_item(const QString& text, int type) : QTableWidgetItem(text, type)
+movie_item::movie_item(const QString& text, int type) : QTableWidgetItem(text, type), movie_item_base()
{
- init_pointers();
}
-movie_item::movie_item(const QIcon& icon, const QString& text, int type) : QTableWidgetItem(icon, text, type)
+movie_item::movie_item(const QIcon& icon, const QString& text, int type) : QTableWidgetItem(icon, text, type), movie_item_base()
{
- init_pointers();
-}
-
-movie_item::~movie_item()
-{
- if (m_movie)
- {
- m_movie->stop();
- }
-
- wait_for_icon_loading(true);
- wait_for_size_on_disk_loading(true);
-}
-
-void movie_item::init_pointers()
-{
- m_icon_loading_aborted.reset(new atomic_t(false));
- m_size_on_disk_loading_aborted.reset(new atomic_t(false));
-}
-
-void movie_item::set_active(bool active)
-{
- if (!std::exchange(m_active, active) && active && m_movie)
- {
- m_movie->jumpToFrame(1);
- m_movie->start();
- }
-}
-
-void movie_item::init_movie(const QString& path)
-{
- if (path.isEmpty() || !m_icon_callback) return;
-
- m_movie.reset(new QMovie(path));
-
- if (!m_movie->isValid())
- {
- m_movie.reset();
- return;
- }
-
- QObject::connect(m_movie.get(), &QMovie::frameChanged, m_movie.get(), m_icon_callback);
-}
-
-void movie_item::call_icon_func() const
-{
- if (m_icon_callback)
- {
- m_icon_callback(0);
- }
-}
-
-void movie_item::set_icon_func(const icon_callback_t& func)
-{
- m_icon_callback = func;
- call_icon_func();
-}
-
-void movie_item::call_icon_load_func(int index)
-{
- if (!m_icon_load_callback || m_icon_loading || m_icon_loading_aborted->load())
- {
- return;
- }
-
- wait_for_icon_loading(true);
-
- *m_icon_loading_aborted = false;
- m_icon_loading = true;
- m_icon_load_thread.reset(QThread::create([this, index]()
- {
- if (m_icon_load_callback)
- {
- m_icon_load_callback(index);
- }
- }));
- m_icon_load_thread->start();
-}
-
-void movie_item::set_icon_load_func(const icon_load_callback_t& func)
-{
- wait_for_icon_loading(true);
-
- m_icon_loading = false;
- m_icon_load_callback = func;
- *m_icon_loading_aborted = false;
-}
-
-void movie_item::call_size_calc_func()
-{
- if (!m_size_calc_callback || m_size_on_disk_loading || m_size_on_disk_loading_aborted->load())
- {
- return;
- }
-
- wait_for_size_on_disk_loading(true);
-
- *m_size_on_disk_loading_aborted = false;
- m_size_on_disk_loading = true;
- m_size_calc_thread.reset(QThread::create([this]()
- {
- if (m_size_calc_callback)
- {
- m_size_calc_callback();
- }
- }));
- m_size_calc_thread->start();
-}
-
-void movie_item::set_size_calc_func(const size_calc_callback_t& func)
-{
- m_size_on_disk_loading = false;
- m_size_calc_callback = func;
- *m_size_on_disk_loading_aborted = false;
-}
-
-void movie_item::wait_for_icon_loading(bool abort)
-{
- *m_icon_loading_aborted = abort;
-
- if (m_icon_load_thread && m_icon_load_thread->isRunning())
- {
- m_icon_load_thread->wait();
- m_icon_load_thread.reset();
- }
-}
-
-void movie_item::wait_for_size_on_disk_loading(bool abort)
-{
- *m_size_on_disk_loading_aborted = abort;
-
- if (m_size_calc_thread && m_size_calc_thread->isRunning())
- {
- m_size_calc_thread->wait();
- m_size_calc_thread.reset();
- }
}
diff --git a/rpcs3/rpcs3qt/movie_item.h b/rpcs3/rpcs3qt/movie_item.h
index ac07560167..348867a63e 100644
--- a/rpcs3/rpcs3qt/movie_item.h
+++ b/rpcs3/rpcs3qt/movie_item.h
@@ -1,89 +1,13 @@
#pragma once
-#include "util/atomic.hpp"
-#include "Utilities/mutex.h"
+#include "movie_item_base.h"
#include
-#include
-#include
-#include
-#include
-#include
-
-using icon_callback_t = std::function;
-using icon_load_callback_t = std::function;
-using size_calc_callback_t = std::function;
-
-class movie_item : public QTableWidgetItem
+class movie_item : public QTableWidgetItem, public movie_item_base
{
public:
movie_item();
movie_item(const QString& text, int type = Type);
movie_item(const QIcon& icon, const QString& text, int type = Type);
- ~movie_item();
-
- void init_pointers();
-
- void set_active(bool active);
-
- [[nodiscard]] bool get_active() const
- {
- return m_active;
- }
-
- [[nodiscard]] std::shared_ptr movie() const
- {
- return m_movie;
- }
-
- void init_movie(const QString& path);
-
- void call_icon_func() const;
- void set_icon_func(const icon_callback_t& func);
-
- void call_icon_load_func(int index);
- void set_icon_load_func(const icon_load_callback_t& func);
-
- void call_size_calc_func();
- void set_size_calc_func(const size_calc_callback_t& func);
-
- void wait_for_icon_loading(bool abort);
- void wait_for_size_on_disk_loading(bool abort);
-
- bool icon_loading() const
- {
- return m_icon_loading;
- }
-
- bool size_on_disk_loading() const
- {
- return m_size_on_disk_loading;
- }
-
- [[nodiscard]] std::shared_ptr> icon_loading_aborted() const
- {
- return m_icon_loading_aborted;
- }
-
- [[nodiscard]] std::shared_ptr> size_on_disk_loading_aborted() const
- {
- return m_size_on_disk_loading_aborted;
- }
-
- shared_mutex pixmap_mutex;
-
-private:
- std::shared_ptr m_movie;
- std::unique_ptr m_icon_load_thread;
- std::unique_ptr m_size_calc_thread;
- bool m_active = false;
- atomic_t m_size_on_disk_loading = false;
- atomic_t m_icon_loading = false;
- size_calc_callback_t m_size_calc_callback = nullptr;
- icon_load_callback_t m_icon_load_callback = nullptr;
- icon_callback_t m_icon_callback = nullptr;
-
- std::shared_ptr> m_icon_loading_aborted;
- std::shared_ptr> m_size_on_disk_loading_aborted;
};
diff --git a/rpcs3/rpcs3qt/movie_item_base.cpp b/rpcs3/rpcs3qt/movie_item_base.cpp
new file mode 100644
index 0000000000..48cd3f5af3
--- /dev/null
+++ b/rpcs3/rpcs3qt/movie_item_base.cpp
@@ -0,0 +1,141 @@
+#include "stdafx.h"
+#include "movie_item_base.h"
+
+movie_item_base::movie_item_base()
+{
+ init_pointers();
+}
+
+movie_item_base::~movie_item_base()
+{
+ if (m_movie)
+ {
+ m_movie->stop();
+ }
+
+ wait_for_icon_loading(true);
+ wait_for_size_on_disk_loading(true);
+}
+
+void movie_item_base::init_pointers()
+{
+ m_icon_loading_aborted.reset(new atomic_t(false));
+ m_size_on_disk_loading_aborted.reset(new atomic_t(false));
+}
+
+void movie_item_base::set_active(bool active)
+{
+ if (!std::exchange(m_active, active) && active && m_movie)
+ {
+ m_movie->jumpToFrame(1);
+ m_movie->start();
+ }
+}
+
+void movie_item_base::init_movie(const QString& path)
+{
+ if (path.isEmpty() || !m_icon_callback) return;
+
+ m_movie.reset(new QMovie(path));
+
+ if (!m_movie->isValid())
+ {
+ m_movie.reset();
+ return;
+ }
+
+ QObject::connect(m_movie.get(), &QMovie::frameChanged, m_movie.get(), m_icon_callback);
+}
+
+void movie_item_base::call_icon_func() const
+{
+ if (m_icon_callback)
+ {
+ m_icon_callback(0);
+ }
+}
+
+void movie_item_base::set_icon_func(const icon_callback_t& func)
+{
+ m_icon_callback = func;
+}
+
+void movie_item_base::call_icon_load_func(int index)
+{
+ if (!m_icon_load_callback || m_icon_loading || m_icon_loading_aborted->load())
+ {
+ return;
+ }
+
+ wait_for_icon_loading(true);
+
+ *m_icon_loading_aborted = false;
+ m_icon_loading = true;
+ m_icon_load_thread.reset(QThread::create([this, index]()
+ {
+ if (m_icon_load_callback)
+ {
+ m_icon_load_callback(index);
+ }
+ }));
+ m_icon_load_thread->start();
+}
+
+void movie_item_base::set_icon_load_func(const icon_load_callback_t& func)
+{
+ wait_for_icon_loading(true);
+
+ m_icon_loading = false;
+ m_icon_load_callback = func;
+ *m_icon_loading_aborted = false;
+}
+
+void movie_item_base::call_size_calc_func()
+{
+ if (!m_size_calc_callback || m_size_on_disk_loading || m_size_on_disk_loading_aborted->load())
+ {
+ return;
+ }
+
+ wait_for_size_on_disk_loading(true);
+
+ *m_size_on_disk_loading_aborted = false;
+ m_size_on_disk_loading = true;
+ m_size_calc_thread.reset(QThread::create([this]()
+ {
+ if (m_size_calc_callback)
+ {
+ m_size_calc_callback();
+ }
+ }));
+ m_size_calc_thread->start();
+}
+
+void movie_item_base::set_size_calc_func(const size_calc_callback_t& func)
+{
+ m_size_on_disk_loading = false;
+ m_size_calc_callback = func;
+ *m_size_on_disk_loading_aborted = false;
+}
+
+void movie_item_base::wait_for_icon_loading(bool abort)
+{
+ *m_icon_loading_aborted = abort;
+
+ if (m_icon_load_thread && m_icon_load_thread->isRunning())
+ {
+ m_icon_load_thread->wait();
+ m_icon_load_thread.reset();
+ }
+}
+
+void movie_item_base::wait_for_size_on_disk_loading(bool abort)
+{
+ *m_size_on_disk_loading_aborted = abort;
+
+ if (m_size_calc_thread && m_size_calc_thread->isRunning())
+ {
+ m_size_calc_thread->wait();
+ m_size_calc_thread.reset();
+ }
+}
diff --git a/rpcs3/rpcs3qt/movie_item_base.h b/rpcs3/rpcs3qt/movie_item_base.h
new file mode 100644
index 0000000000..ae6071eadc
--- /dev/null
+++ b/rpcs3/rpcs3qt/movie_item_base.h
@@ -0,0 +1,88 @@
+#pragma once
+
+#include "movie_item_base.h"
+#include "util/atomic.hpp"
+#include "Utilities/mutex.h"
+
+#include
+#include
+
+#include
+#include
+
+using icon_callback_t = std::function;
+using icon_load_callback_t = std::function;
+using size_calc_callback_t = std::function;
+
+class movie_item_base
+{
+public:
+ movie_item_base();
+ virtual ~movie_item_base();
+
+ void init_pointers();
+
+ void set_active(bool active);
+
+ [[nodiscard]] bool get_active() const
+ {
+ return m_active;
+ }
+
+ [[nodiscard]] std::shared_ptr movie() const
+ {
+ return m_movie;
+ }
+
+ void init_movie(const QString& path);
+
+ void call_icon_func() const;
+ void set_icon_func(const icon_callback_t& func);
+
+ void call_icon_load_func(int index);
+ void set_icon_load_func(const icon_load_callback_t& func);
+
+ void call_size_calc_func();
+ void set_size_calc_func(const size_calc_callback_t& func);
+
+ void wait_for_icon_loading(bool abort);
+ void wait_for_size_on_disk_loading(bool abort);
+
+ bool icon_loading() const
+ {
+ return m_icon_loading;
+ }
+
+ bool size_on_disk_loading() const
+ {
+ return m_size_on_disk_loading;
+ }
+
+ [[nodiscard]] std::shared_ptr> icon_loading_aborted() const
+ {
+ return m_icon_loading_aborted;
+ }
+
+ [[nodiscard]] std::shared_ptr> size_on_disk_loading_aborted() const
+ {
+ return m_size_on_disk_loading_aborted;
+ }
+
+ shared_mutex pixmap_mutex;
+
+protected:
+ std::shared_ptr m_movie;
+
+private:
+ std::unique_ptr m_icon_load_thread;
+ std::unique_ptr m_size_calc_thread;
+ bool m_active = false;
+ atomic_t m_size_on_disk_loading = false;
+ atomic_t m_icon_loading = false;
+ size_calc_callback_t m_size_calc_callback = nullptr;
+ icon_load_callback_t m_icon_load_callback = nullptr;
+ icon_callback_t m_icon_callback = nullptr;
+
+ std::shared_ptr> m_icon_loading_aborted;
+ std::shared_ptr> m_size_on_disk_loading_aborted;
+};
diff --git a/rpcs3/rpcs3qt/qt_utils.cpp b/rpcs3/rpcs3qt/qt_utils.cpp
index 23719eff58..a179b28cb0 100644
--- a/rpcs3/rpcs3qt/qt_utils.cpp
+++ b/rpcs3/rpcs3qt/qt_utils.cpp
@@ -198,15 +198,15 @@ namespace gui
return l.sizeHint().width();
}
- QImage get_centered_image(const QString& path, const QSize& icon_size, int offset_x, int offset_y, qreal device_pixel_ratio)
+ QPixmap get_centered_pixmap(QPixmap pixmap, const QSize& icon_size, int offset_x, int offset_y, qreal device_pixel_ratio, Qt::TransformationMode mode)
{
// Create empty canvas for expanded image
- QImage exp_img(icon_size, QImage::Format_ARGB32);
+ QPixmap exp_img(icon_size);
exp_img.setDevicePixelRatio(device_pixel_ratio);
exp_img.fill(Qt::transparent);
// Load scaled pixmap
- const QPixmap pixmap = QPixmap(path).scaled(icon_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ pixmap = pixmap.scaled(icon_size, Qt::KeepAspectRatio, mode);
// Define offset for raw image placement
QPoint offset(offset_x + icon_size.width() / 2 - pixmap.width() / 2,
@@ -221,9 +221,9 @@ namespace gui
return exp_img;
}
- QPixmap get_centered_pixmap(const QString& path, const QSize& icon_size, int offset_x, int offset_y, qreal device_pixel_ratio)
+ QPixmap get_centered_pixmap(const QString& path, const QSize& icon_size, int offset_x, int offset_y, qreal device_pixel_ratio, Qt::TransformationMode mode)
{
- return QPixmap::fromImage(get_centered_image(path, icon_size, offset_x, offset_y, device_pixel_ratio));
+ return get_centered_pixmap(QPixmap(path), icon_size, offset_x, offset_y, device_pixel_ratio, mode);
}
QImage get_opaque_image_area(const QString& path)
diff --git a/rpcs3/rpcs3qt/qt_utils.h b/rpcs3/rpcs3qt/qt_utils.h
index 9a15f68ddd..32312b34ae 100644
--- a/rpcs3/rpcs3qt/qt_utils.h
+++ b/rpcs3/rpcs3qt/qt_utils.h
@@ -82,11 +82,11 @@ namespace gui
qobj.setFont(font);
}
- // Returns a scaled, centered QImage
- QImage get_centered_image(const QString& path, const QSize& icon_size, int offset_x, int offset_y, qreal device_pixel_ratio);
+ // Returns a scaled, centered QPixmap
+ QPixmap get_centered_pixmap(QPixmap pixmap, const QSize& icon_size, int offset_x, int offset_y, qreal device_pixel_ratio, Qt::TransformationMode mode);
// Returns a scaled, centered QPixmap
- QPixmap get_centered_pixmap(const QString& path, const QSize& icon_size, int offset_x, int offset_y, qreal device_pixel_ratio);
+ QPixmap get_centered_pixmap(const QString& path, const QSize& icon_size, int offset_x, int offset_y, qreal device_pixel_ratio, Qt::TransformationMode mode);
// Returns the part of the image loaded from path that is inside the bounding box of its opaque areas
QImage get_opaque_image_area(const QString& path);
diff --git a/rpcs3/rpcs3qt/render_creator.cpp b/rpcs3/rpcs3qt/render_creator.cpp
index ba6eff83aa..c939c729fb 100644
--- a/rpcs3/rpcs3qt/render_creator.cpp
+++ b/rpcs3/rpcs3qt/render_creator.cpp
@@ -16,8 +16,6 @@
LOG_CHANNEL(cfg_log, "CFG");
-constexpr auto qstr = QString::fromStdString;
-
render_creator::render_creator(QObject *parent) : QObject(parent)
{
#if defined(HAVE_VULKAN)
@@ -50,9 +48,9 @@ render_creator::render_creator(QObject *parent) : QObject(parent)
if (!work_done) // The spawning thread gave up, do not attempt to modify vulkan_adapters
{
- for (auto& gpu : gpus)
+ for (const auto& gpu : gpus)
{
- adapters->append(qstr(gpu.get_name()));
+ adapters->append(QString::fromStdString(gpu.get_name()));
}
}
}
diff --git a/rpcs3/rpcs3qt/screenshot_item.cpp b/rpcs3/rpcs3qt/screenshot_item.cpp
new file mode 100644
index 0000000000..d0916f7114
--- /dev/null
+++ b/rpcs3/rpcs3qt/screenshot_item.cpp
@@ -0,0 +1,32 @@
+#include "screenshot_item.h"
+#include "qt_utils.h"
+
+#include
+
+screenshot_item::screenshot_item(QWidget* parent)
+ : flow_widget_item(parent)
+{
+ cb_on_first_visibility = [this]()
+ {
+ m_thread.reset(QThread::create([this]()
+ {
+ const QPixmap pixmap = gui::utils::get_centered_pixmap(icon_path, icon_size, 0, 0, 1.0, Qt::SmoothTransformation);
+ Q_EMIT signal_icon_update(pixmap);
+ }));
+ m_thread->start();
+ };
+
+ label = new QLabel(this);
+ QVBoxLayout* layout = new QVBoxLayout(this);
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->addWidget(label);
+ setLayout(layout);
+}
+
+screenshot_item::~screenshot_item()
+{
+ if (m_thread && m_thread->isRunning())
+ {
+ m_thread->wait();
+ }
+}
diff --git a/rpcs3/rpcs3qt/screenshot_item.h b/rpcs3/rpcs3qt/screenshot_item.h
new file mode 100644
index 0000000000..36f0232b1f
--- /dev/null
+++ b/rpcs3/rpcs3qt/screenshot_item.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "flow_widget_item.h"
+#include
+#include
+
+class screenshot_item : public flow_widget_item
+{
+ Q_OBJECT
+
+public:
+ screenshot_item(QWidget* parent);
+ virtual ~screenshot_item();
+
+ QString icon_path;
+ QSize icon_size;
+ QLabel* label{};
+
+private:
+ std::unique_ptr m_thread;
+
+Q_SIGNALS:
+ void signal_icon_update(const QPixmap& pixmap);
+};
diff --git a/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp b/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp
index f25aee4494..5564bd4bf4 100644
--- a/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp
+++ b/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp
@@ -1,6 +1,7 @@
#include "stdafx.h"
#include "screenshot_manager_dialog.h"
#include "screenshot_preview.h"
+#include "screenshot_item.h"
#include "flow_widget.h"
#include "qt_utils.h"
#include "Utilities/File.h"
@@ -23,7 +24,7 @@ screenshot_manager_dialog::screenshot_manager_dialog(QWidget* parent) : QDialog(
m_icon_size = QSize(160, 90);
m_flow_widget = new flow_widget(this);
- m_flow_widget->setObjectName("m_flow_widget");
+ m_flow_widget->setObjectName("flow_widget");
m_placeholder = QPixmap(m_icon_size);
m_placeholder.fill(Qt::gray);
@@ -137,31 +138,3 @@ bool screenshot_manager_dialog::eventFilter(QObject* watched, QEvent* event)
return false;
}
-
-screenshot_item::screenshot_item(QWidget* parent)
- : flow_widget_item(parent)
-{
- cb_on_first_visibility = [this]()
- {
- m_thread.reset(QThread::create([this]()
- {
- const QPixmap pixmap = gui::utils::get_centered_pixmap(icon_path, icon_size, 0, 0, 1.0);
- Q_EMIT signal_icon_update(pixmap);
- }));
- m_thread->start();
- };
-
- label = new QLabel(this);
- QVBoxLayout* layout = new QVBoxLayout(this);
- layout->setContentsMargins(0, 0, 0, 0);
- layout->addWidget(label);
- setLayout(layout);
-}
-
-screenshot_item::~screenshot_item()
-{
- if (m_thread && m_thread->isRunning())
- {
- m_thread->wait();
- }
-}
diff --git a/rpcs3/rpcs3qt/screenshot_manager_dialog.h b/rpcs3/rpcs3qt/screenshot_manager_dialog.h
index 46f908d4dc..ab4d7c13a8 100644
--- a/rpcs3/rpcs3qt/screenshot_manager_dialog.h
+++ b/rpcs3/rpcs3qt/screenshot_manager_dialog.h
@@ -6,9 +6,7 @@
#include
#include
#include
-#include
#include
-#include
#include
class screenshot_manager_dialog : public QDialog
@@ -46,22 +44,3 @@ private:
QSize m_icon_size;
QPixmap m_placeholder;
};
-
-class screenshot_item : public flow_widget_item
-{
- Q_OBJECT
-
-public:
- screenshot_item(QWidget* parent);
- virtual ~screenshot_item();
-
- QString icon_path;
- QSize icon_size;
- QLabel* label{};
-
-private:
- std::unique_ptr m_thread;
-
-Q_SIGNALS:
- void signal_icon_update(const QPixmap& pixmap);
-};
diff --git a/rpcs3/rpcs3qt/stylesheets.h b/rpcs3/rpcs3qt/stylesheets.h
index 9121d03609..15c32b7473 100644
--- a/rpcs3/rpcs3qt/stylesheets.h
+++ b/rpcs3/rpcs3qt/stylesheets.h
@@ -29,6 +29,20 @@ namespace gui
// game list icon color
"QLabel#gamelist_icon_background_color { color: rgba(240, 240, 240, 255); }"
+ // game grid
+ "#game_list_grid #flow_widget_content { background: #fff; }"
+ "#game_list_grid_item { background: #fff; }"
+ "#game_list_grid_item[selected=\"true\"] { background: #148aff; }"
+ "#game_list_grid_item:focus { background: #148aff; }"
+ "#game_list_grid_item:hover { background: #94c9ff; }"
+ "#game_list_grid_item:hover:focus { background: #007fff; }"
+ "#game_list_grid_item #game_list_grid_item_title_label { color: rgba(51, 51, 51, 255); font-weight: 600; font-size: 8pt; font-family: Lucida Grande; border: 0em solid white; }"
+
+ // game grid hover and focus: we need to handle properties differently when using descendants
+ "#game_list_grid_item[selected=\"true\"] #game_list_grid_item_title_label { color: #fff; }"
+ "#game_list_grid_item[hover=\"true\"] #game_list_grid_item_title_label { color: #fff; }"
+ "#game_list_grid_item[focus=\"true\"] #game_list_grid_item_title_label { color: #fff; }"
+
// save manager icon color
"QLabel#save_manager_icon_background_color { color: rgba(240, 240, 240, 255); }"
@@ -37,11 +51,8 @@ namespace gui
// tables
"QTableWidget { alternate-background-color: #f2f2f2; background-color: #fff; border: none; }"
- "QTableWidget#game_grid { alternate-background-color: #f2f2f2; background-color: #fff; font-weight: 600; font-size: 8pt; font-family: Lucida Grande; color: rgba(51, 51, 51, 255); border: 0em solid white; }"
"QTableView::item { border-left: 0.063em solid white; border-right: 0.063em solid white; padding-left:0.313em; }"
"QTableView::item:selected { background-color: #148aff; color: #fff; }"
- "QTableView#game_grid::item:hover:!selected { background-color: #94c9ff; color: #fff; }"
- "QTableView#game_grid::item:hover:selected { background-color: #007fff; color: #fff; }"
// table headers
"QHeaderView::section { padding-left: .5em; padding-right: .5em; padding-top: .4em; padding-bottom: -.1em; border: 0.063em solid #ffffff; }"