Qt: implement flow layout game grid

This will allow us to properly style the grid and also remove the need to refresh the whole grid on a window resize
This commit is contained in:
Megamouse 2023-04-28 16:09:08 +02:00
parent 0b628cb50e
commit f115032095
48 changed files with 2563 additions and 1601 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -234,6 +234,12 @@
<ClCompile Include="QTGeneratedFiles\Debug\moc_find_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_flow_widget.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_flow_widget_item.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_game_compatibility.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -246,6 +252,12 @@
<ClCompile Include="QTGeneratedFiles\Debug\moc_game_list_grid.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_game_list_grid_item.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_game_list_table.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_gs_frame.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -348,6 +360,9 @@
<ClCompile Include="QTGeneratedFiles\Debug\moc_save_manager_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_screenshot_item.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_screenshot_manager_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -471,6 +486,12 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_find_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_flow_widget.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_flow_widget_item.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_game_compatibility.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -483,6 +504,12 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_game_list_grid.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_game_list_grid_item.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_game_list_table.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_gs_frame.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -585,6 +612,9 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_save_manager_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_screenshot_item.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_screenshot_manager_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -667,8 +697,12 @@
<ClCompile Include="rpcs3qt\fatal_error_dialog.cpp" />
<ClCompile Include="rpcs3qt\flow_layout.cpp" />
<ClCompile Include="rpcs3qt\flow_widget.cpp" />
<ClCompile Include="rpcs3qt\flow_widget_item.cpp" />
<ClCompile Include="rpcs3qt\game_list.cpp" />
<ClCompile Include="rpcs3qt\game_list_base.cpp" />
<ClCompile Include="rpcs3qt\game_list_delegate.cpp" />
<ClCompile Include="rpcs3qt\game_list_grid_item.cpp" />
<ClCompile Include="rpcs3qt\game_list_table.cpp" />
<ClCompile Include="rpcs3qt\gui_application.cpp" />
<ClCompile Include="rpcs3qt\input_dialog.cpp" />
<ClCompile Include="rpcs3qt\ipc_settings_dialog.cpp" />
@ -676,6 +710,7 @@
<ClCompile Include="rpcs3qt\log_viewer.cpp" />
<ClCompile Include="rpcs3qt\microphone_creator.cpp" />
<ClCompile Include="rpcs3qt\movie_item.cpp" />
<ClCompile Include="rpcs3qt\movie_item_base.cpp" />
<ClCompile Include="rpcs3qt\osk_dialog_frame.cpp" />
<ClCompile Include="rpcs3qt\pad_led_settings_dialog.cpp" />
<ClCompile Include="rpcs3qt\pad_motion_settings_dialog.cpp" />
@ -691,6 +726,7 @@
<ClCompile Include="rpcs3qt\recvmessage_dialog_frame.cpp" />
<ClCompile Include="rpcs3qt\render_creator.cpp" />
<ClCompile Include="rpcs3qt\rpcn_settings_dialog.cpp" />
<ClCompile Include="rpcs3qt\screenshot_item.cpp" />
<ClCompile Include="rpcs3qt\screenshot_manager_dialog.cpp" />
<ClCompile Include="rpcs3qt\screenshot_preview.cpp" />
<ClCompile Include="rpcs3qt\sendmessage_dialog_frame.cpp" />
@ -713,7 +749,6 @@
<ClCompile Include="rpcs3qt\find_dialog.cpp" />
<ClCompile Include="rpcs3qt\game_compatibility.cpp" />
<ClCompile Include="rpcs3qt\game_list_grid.cpp" />
<ClCompile Include="rpcs3qt\game_list_grid_delegate.cpp" />
<ClCompile Include="rpcs3qt\progress_dialog.cpp" />
<ClCompile Include="rpcs3qt\qt_utils.cpp" />
<ClCompile Include="rpcs3qt\syntax_highlighter.cpp" />
@ -1148,6 +1183,16 @@
</CustomBuild>
<ClInclude Include="rpcs3qt\flow_layout.h" />
<CustomBuild Include="rpcs3qt\flow_widget.h">
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(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"</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(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"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
</CustomBuild>
<CustomBuild Include="rpcs3qt\game_list_base.h">
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -1161,7 +1206,37 @@
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
</Command>
</CustomBuild>
<CustomBuild Include="rpcs3qt\flow_widget_item.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(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"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(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"</Command>
</CustomBuild>
<ClInclude Include="rpcs3qt\game_list_delegate.h" />
<CustomBuild Include="rpcs3qt\game_list_table.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(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"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(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"</Command>
</CustomBuild>
<CustomBuild Include="rpcs3qt\game_list_grid_item.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(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"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(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"</Command>
</CustomBuild>
<ClInclude Include="rpcs3qt\gui_save.h" />
<CustomBuild Include="rpcs3qt\patch_manager_dialog.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
@ -1214,6 +1289,7 @@
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(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"</Command>
</CustomBuild>
<ClInclude Include="rpcs3qt\movie_item.h" />
<ClInclude Include="rpcs3qt\movie_item_base.h" />
<ClInclude Include="rpcs3qt\numbered_widget_item.h" />
<CustomBuild Include="rpcs3qt\patch_creator_dialog.h">
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
@ -1310,6 +1386,16 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(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"</Command>
</CustomBuild>
<CustomBuild Include="rpcs3qt\screenshot_item.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(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"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(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"</Command>
</CustomBuild>
<ClInclude Include="rpcs3qt\shortcut_utils.h" />
<ClInclude Include="rpcs3qt\stylesheets.h" />
<CustomBuild Include="rpcs3qt\skylander_dialog.h">
@ -1416,7 +1502,6 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(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"</Command>
</CustomBuild>
<ClInclude Include="rpcs3qt\game_list_grid_delegate.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="rpcs3qt\gl_gs_frame.h" />
<CustomBuild Include="rpcs3qt\syntax_highlighter.h">
@ -1752,6 +1837,17 @@
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<None Include="..\bin\GuiConfigs\Classic (Bright).qss" />
<None Include="..\bin\GuiConfigs\Darker Style by TheMitoSan.qss" />
<None Include="..\bin\GuiConfigs\Envy.qss" />
<None Include="..\bin\GuiConfigs\Kuroi (Dark) by Ani.qss" />
<None Include="..\bin\GuiConfigs\ModernBlue Theme by TheMitoSan.qss" />
<None Include="..\bin\GuiConfigs\Nekotekina by GooseWing.qss" />
<None Include="..\bin\GuiConfigs\Skyline (Nightfall).qss" />
<None Include="..\bin\GuiConfigs\Skyline.qss" />
<None Include="..\bin\GuiConfigs\YoRHa by Ani.qss" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
<ProjectExtensions>

View File

@ -154,6 +154,9 @@
<Filter Include="Gui\flow_layout">
<UniqueIdentifier>{9191ae51-8b80-4606-b5bc-966a9588f538}</UniqueIdentifier>
</Filter>
<Filter Include="StyleSheets">
<UniqueIdentifier>{b927140a-5214-4628-a648-8380ae793179}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
@ -381,9 +384,6 @@
<ClCompile Include="rpcs3qt\game_list_grid.cpp">
<Filter>Gui\game list</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\game_list_grid_delegate.cpp">
<Filter>Gui\game list</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\memory_string_searcher.cpp">
<Filter>Gui\dev tools</Filter>
</ClCompile>
@ -954,6 +954,54 @@
<ClCompile Include="rpcs3qt\flow_widget.cpp">
<Filter>Gui\flow_layout</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\game_list_table.cpp">
<Filter>Gui\game list</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_game_list_table.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_game_list_table.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\game_list_base.cpp">
<Filter>Gui\game list</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\movie_item_base.cpp">
<Filter>Gui\custom items</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\game_list_grid_item.cpp">
<Filter>Gui\game list</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_game_list_grid_item.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_game_list_grid_item.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\flow_widget_item.cpp">
<Filter>Gui\flow_layout</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_screenshot_item.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_screenshot_item.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\screenshot_item.cpp">
<Filter>Gui\screenshot manager</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_flow_widget_item.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_flow_widget_item.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_flow_widget.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_flow_widget.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Input\ds4_pad_handler.h">
@ -992,9 +1040,6 @@
<ClInclude Include="rpcs3qt\gl_gs_frame.h">
<Filter>Gui\game window</Filter>
</ClInclude>
<ClInclude Include="rpcs3qt\game_list_grid_delegate.h">
<Filter>Gui\game list</Filter>
</ClInclude>
<ClInclude Include="Input\evdev_joystick_handler.h">
<Filter>Io\evdev</Filter>
</ClInclude>
@ -1130,6 +1175,9 @@
<ClInclude Include="rpcs3qt\flow_layout.h">
<Filter>Gui\flow_layout</Filter>
</ClInclude>
<ClInclude Include="rpcs3qt\movie_item_base.h">
<Filter>Gui\custom items</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="debug\moc_predefs.h.cbt">
@ -1408,6 +1456,21 @@
<CustomBuild Include="rpcs3qt\flow_widget.h">
<Filter>Gui\flow_layout</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\game_list_table.h">
<Filter>Gui\game list</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\game_list_base.h">
<Filter>Gui\game list</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\game_list_grid_item.h">
<Filter>Gui\game list</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\screenshot_item.h">
<Filter>Gui\screenshot manager</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\flow_widget_item.h">
<Filter>Gui\flow_layout</Filter>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<Image Include="rpcs3.ico" />
@ -1415,4 +1478,33 @@
<ItemGroup>
<ResourceCompile Include="rpcs3.rc" />
</ItemGroup>
<ItemGroup>
<None Include="..\bin\GuiConfigs\Classic (Bright).qss">
<Filter>StyleSheets</Filter>
</None>
<None Include="..\bin\GuiConfigs\Darker Style by TheMitoSan.qss">
<Filter>StyleSheets</Filter>
</None>
<None Include="..\bin\GuiConfigs\YoRHa by Ani.qss">
<Filter>StyleSheets</Filter>
</None>
<None Include="..\bin\GuiConfigs\Skyline.qss">
<Filter>StyleSheets</Filter>
</None>
<None Include="..\bin\GuiConfigs\ModernBlue Theme by TheMitoSan.qss">
<Filter>StyleSheets</Filter>
</None>
<None Include="..\bin\GuiConfigs\Nekotekina by GooseWing.qss">
<Filter>StyleSheets</Filter>
</None>
<None Include="..\bin\GuiConfigs\Skyline (Nightfall).qss">
<Filter>StyleSheets</Filter>
</None>
<None Include="..\bin\GuiConfigs\Kuroi (Dark) by Ani.qss">
<Filter>StyleSheets</Filter>
</None>
<None Include="..\bin\GuiConfigs\Envy.qss">
<Filter>StyleSheets</Filter>
</None>
</ItemGroup>
</Project>

View File

@ -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

View File

@ -51,14 +51,23 @@
#include <QtWidgets>
#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<QWidget*>(parent);
const QWidget* pw = static_cast<const QWidget*>(parent);
return pw->style()->pixelMetric(pm, nullptr, pw);
}
return static_cast<QLayout*>(parent)->spacing();
return static_cast<const QLayout*>(parent)->spacing();
}

View File

@ -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<QLayoutItem*>& item_list() const { return itemList; }
const QList<position>& 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<QLayoutItem*> 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<position> m_positions;
mutable int m_rows{};
mutable int m_cols{};
};

View File

@ -1,8 +1,10 @@
#include "stdafx.h"
#include "flow_widget.h"
#include "flow_layout.h"
#include <QScrollArea>
#include <QVBoxLayout>
#include <QStyleOption>
#include <QPainter>
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_item*>& 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<QWidget*>(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<flow_widget_item*>(QObject::sender()));
}
void flow_widget::on_navigate(flow_navigation value)
{
const flow_layout::position selected_pos = find_next_item(find_item(static_cast<flow_widget_item*>(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;
}

View File

@ -1,21 +1,16 @@
#pragma once
#include "flow_widget_item.h"
#include "flow_layout.h"
#include <QWidget>
class flow_layout;
class flow_widget_item : public QWidget
{
public:
using QWidget::QWidget;
void paintEvent(QPaintEvent* event) override;
bool got_visible{};
std::function<void()> cb_on_first_visibility{};
};
#include <QScrollArea>
#include <QPaintEvent>
class flow_widget : public QWidget
{
Q_OBJECT
public:
flow_widget(QWidget* parent);
virtual ~flow_widget();
@ -24,8 +19,28 @@ public:
void clear();
QList<flow_widget_item*>& 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<flow_widget_item*> m_widgets{};
int m_selected_index = -1;
};

View File

@ -0,0 +1,116 @@
#include "flow_widget_item.h"
#include <QStyle>
#include <QStyleOption>
#include <QPainter>
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<QWidget*>(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);
}

View File

@ -0,0 +1,48 @@
#pragma once
#include <QWidget>
#include <QKeyEvent>
#include <functional>
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<void()> cb_on_first_visibility{};
protected:
bool m_hover{};
Q_SIGNALS:
void navigate(flow_navigation value);
void focused();
};

View File

@ -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;

View File

@ -3,46 +3,31 @@
#include <QTableWidget>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QPixmap>
#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<gui_game_info> 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;

View File

@ -0,0 +1,227 @@
#include "stdafx.h"
#include "game_list_base.h"
#include "localized.h"
#include <QDir>
#include <cmath>
#include <unordered_set>
LOG_CHANNEL(game_list_log, "GameList");
game_list_base::game_list_base()
{
}
void game_list_base::repaint_icons(QList<game_info>& 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<atomic_t<bool>> cancel)
{
if (cancel && cancel->load())
{
return;
}
static std::unordered_set<std::string> 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<double>(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<int>(0, (canvas_size.width() - icon_size.width()) / 2.0));
target_pos.setY(std::max<int>(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 {};
}

View File

@ -0,0 +1,63 @@
#pragma once
#include "movie_item_base.h"
#include "game_compatibility.h"
#include "Emu/GameInfo.h"
#include <QIcon>
#include <QPixmap>
#include <QWidget>
/* 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<gui_game_info> 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_info>& game_data,
[[maybe_unused]] const QMap<QString, QString>& notes_map,
[[maybe_unused]] const QMap<QString, QString>& 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_info>& 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<atomic_t<bool>> 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<void(const game_info&)> m_icon_ready_callback{};
bool m_draw_compat_status_to_grid{};
bool m_is_list_layout{};
QSize m_icon_size{};
QColor m_icon_color{};
};

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,7 @@
#include <memory>
#include <set>
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> gui_settings, std::shared_ptr<emu_settings> emu_settings, std::shared_ptr<persistent_settings> 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<game_info>& 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<gui::utils::shortcut_location>& 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<std::string>& 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<atomic_t<bool>> 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<u32, QString>& 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<QAction*> 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<QString, QString> m_notes;
QMap<QString, QString> m_titles;

View File

@ -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 <QHeaderView>
#include <QScrollBar>
#include <QApplication>
#include <QStringBuilder>
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<game_list_grid_item*>(::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<QMovie> 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_info>& game_data,
const QMap<QString, QString>& notes_map,
const QMap<QString, QString>& 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<QMovie> 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_info>& 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_list_grid_item*>(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<QMouseEvent*>(event)->button() == Qt::LeftButton)
{
if (game_list_grid_item* item = static_cast<game_list_grid_item*>(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);
}

View File

@ -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 <QKeyEvent>
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_info>& game_data,
const QMap<QString, QString>& notes_map,
const QMap<QString, QString>& 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_info>& 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);
};

View File

@ -1,88 +0,0 @@
#include "game_list_grid_delegate.h"
#include "movie_item.h"
#include <QTableWidget>
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<const QTableWidget*>(parent()))
{
if (movie_item* item = static_cast<movie_item*>(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<QPixmap>(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;
}

View File

@ -1,19 +0,0 @@
#pragma once
#include <QPainter>
#include <QStyledItemDelegate>
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;
};

View File

@ -0,0 +1,86 @@
#include "game_list_grid_item.h"
#include <QVBoxLayout>
#include <QStyle>
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);
}

View File

@ -0,0 +1,36 @@
#pragma once
#include "flow_widget_item.h"
#include "movie_item_base.h"
#include "game_list_base.h"
#include <QLabel>
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{};
};

View File

@ -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 <QApplication>
#include <QHeaderView>
#include <QScrollBar>
#include <QStringBuilder>
game_list_table::game_list_table(game_list_frame* frame, std::shared_ptr<persistent_settings> 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<movie_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<qulonglong>(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<int> 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<int> 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_info>& game_data,
const QMap<QString, QString>& notes_map,
const QMap<QString, QString>& title_map,
const std::string& selected_item_id,
bool play_hover_movies)
{
clear_list();
setRowCount(::narrow<int>(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<QMovie> 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<s32>::min(), ::std::numeric_limits<s32>::max());
const bool ok_top = !unknown && try_to_float(&top_ver, game->compat.latest_version.toStdString(), ::std::numeric_limits<s32>::min(), ::std::numeric_limits<s32>::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<qulonglong>(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_info>& 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();
}

View File

@ -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> 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_info>& game_data,
const QMap<QString, QString>& notes_map,
const QMap<QString, QString>& title_map,
const std::string& selected_item_id,
bool play_hover_movies) override;
void repaint_icons(QList<game_info>& 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<persistent_settings> m_persistent_settings;
};

View File

@ -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<u32, QString>& 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") },

View File

@ -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<u32, QString>& map, bool combined = false);
const struct category // (see PARAM.SFO in psdevwiki.com) TODO: Disc Categories
{

View File

@ -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));
}
}
}

View File

@ -8,11 +8,6 @@
#include "Utilities/Thread.h"
#include "Utilities/StrUtil.h"
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QHBoxLayout>
#include <QComboBox>
#include <QCheckBox>
#include <charconv>
@ -24,8 +19,6 @@
LOG_CHANNEL(gui_log, "GUI");
constexpr auto qstr = QString::fromStdString;
template <>
void fmt_class_string<search_mode>::format(std::string& out, u64 arg)
{

View File

@ -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<bool>(false));
m_size_on_disk_loading_aborted.reset(new atomic_t<bool>(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();
}
}

View File

@ -1,89 +1,13 @@
#pragma once
#include "util/atomic.hpp"
#include "Utilities/mutex.h"
#include "movie_item_base.h"
#include <QTableWidgetItem>
#include <QMovie>
#include <QObject>
#include <QThread>
#include <memory>
#include <functional>
using icon_callback_t = std::function<void(int)>;
using icon_load_callback_t = std::function<void(int)>;
using size_calc_callback_t = std::function<void()>;
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<QMovie> 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<atomic_t<bool>> icon_loading_aborted() const
{
return m_icon_loading_aborted;
}
[[nodiscard]] std::shared_ptr<atomic_t<bool>> size_on_disk_loading_aborted() const
{
return m_size_on_disk_loading_aborted;
}
shared_mutex pixmap_mutex;
private:
std::shared_ptr<QMovie> m_movie;
std::unique_ptr<QThread> m_icon_load_thread;
std::unique_ptr<QThread> m_size_calc_thread;
bool m_active = false;
atomic_t<bool> m_size_on_disk_loading = false;
atomic_t<bool> 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<atomic_t<bool>> m_icon_loading_aborted;
std::shared_ptr<atomic_t<bool>> m_size_on_disk_loading_aborted;
};

View File

@ -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<bool>(false));
m_size_on_disk_loading_aborted.reset(new atomic_t<bool>(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();
}
}

View File

@ -0,0 +1,88 @@
#pragma once
#include "movie_item_base.h"
#include "util/atomic.hpp"
#include "Utilities/mutex.h"
#include <QMovie>
#include <QThread>
#include <memory>
#include <functional>
using icon_callback_t = std::function<void(int)>;
using icon_load_callback_t = std::function<void(int)>;
using size_calc_callback_t = std::function<void()>;
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<QMovie> 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<atomic_t<bool>> icon_loading_aborted() const
{
return m_icon_loading_aborted;
}
[[nodiscard]] std::shared_ptr<atomic_t<bool>> size_on_disk_loading_aborted() const
{
return m_size_on_disk_loading_aborted;
}
shared_mutex pixmap_mutex;
protected:
std::shared_ptr<QMovie> m_movie;
private:
std::unique_ptr<QThread> m_icon_load_thread;
std::unique_ptr<QThread> m_size_calc_thread;
bool m_active = false;
atomic_t<bool> m_size_on_disk_loading = false;
atomic_t<bool> 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<atomic_t<bool>> m_icon_loading_aborted;
std::shared_ptr<atomic_t<bool>> m_size_on_disk_loading_aborted;
};

View File

@ -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)

View File

@ -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);

View File

@ -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()));
}
}
}

View File

@ -0,0 +1,32 @@
#include "screenshot_item.h"
#include "qt_utils.h"
#include <QVBoxLayout>
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();
}
}

View File

@ -0,0 +1,24 @@
#pragma once
#include "flow_widget_item.h"
#include <QLabel>
#include <QThread>
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<QThread> m_thread;
Q_SIGNALS:
void signal_icon_update(const QPixmap& pixmap);
};

View File

@ -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();
}
}

View File

@ -6,9 +6,7 @@
#include <QFutureWatcher>
#include <QPixmap>
#include <QSize>
#include <QLabel>
#include <QEvent>
#include <QThread>
#include <array>
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<QThread> m_thread;
Q_SIGNALS:
void signal_icon_update(const QPixmap& pixmap);
};

View File

@ -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; }"