mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-06-06 23:25:02 -06:00
Qt: Add mulit selection to game grid
This commit is contained in:
parent
1bfd1154f5
commit
72a19a87b6
@ -61,6 +61,8 @@ public:
|
|||||||
{
|
{
|
||||||
int row{};
|
int row{};
|
||||||
int col{};
|
int col{};
|
||||||
|
|
||||||
|
operator bool() const { return row >= 0 && col >= 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit flow_layout(QWidget* parent, int margin = -1, bool dynamic_spacing = false, int hSpacing = -1, int vSpacing = -1);
|
explicit flow_layout(QWidget* parent, int margin = -1, bool dynamic_spacing = false, int hSpacing = -1, int vSpacing = -1);
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "flow_widget.h"
|
#include "flow_widget.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
#include <QScrollArea>
|
#include <QScrollArea>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QStyleOption>
|
#include <QStyleOption>
|
||||||
@ -48,14 +49,24 @@ void flow_widget::clear()
|
|||||||
m_flow_layout->clear();
|
m_flow_layout->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
flow_widget_item* flow_widget::selected_item() const
|
std::set<flow_widget_item*> flow_widget::selected_items() const
|
||||||
{
|
{
|
||||||
if (m_selected_index >= 0 && static_cast<usz>(m_selected_index) < m_widgets.size())
|
std::set<flow_widget_item*> items;
|
||||||
|
|
||||||
|
for (s64 index : m_selected_items)
|
||||||
{
|
{
|
||||||
return ::at32(m_widgets, m_selected_index);
|
if (index >= 0 && static_cast<usz>(index) < m_widgets.size())
|
||||||
|
{
|
||||||
|
items.insert(::at32(m_widgets, index));
|
||||||
|
|
||||||
|
if (!m_allow_multi_selection)
|
||||||
|
{
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
void flow_widget::paintEvent(QPaintEvent* /*event*/)
|
void flow_widget::paintEvent(QPaintEvent* /*event*/)
|
||||||
@ -69,7 +80,7 @@ void flow_widget::paintEvent(QPaintEvent* /*event*/)
|
|||||||
|
|
||||||
s64 flow_widget::find_item(const flow_layout::position& pos)
|
s64 flow_widget::find_item(const flow_layout::position& pos)
|
||||||
{
|
{
|
||||||
if (pos.row < 0 || pos.col < 0)
|
if (!pos)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -109,7 +120,7 @@ flow_layout::position flow_widget::find_item(flow_widget_item* item)
|
|||||||
|
|
||||||
flow_layout::position flow_widget::find_next_item(flow_layout::position current_pos, flow_navigation value)
|
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)
|
if (current_pos && m_flow_layout->rows() > 0 && m_flow_layout->cols() > 0)
|
||||||
{
|
{
|
||||||
switch (value)
|
switch (value)
|
||||||
{
|
{
|
||||||
@ -183,54 +194,160 @@ flow_layout::position flow_widget::find_next_item(flow_layout::position current_
|
|||||||
return current_pos;
|
return current_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void flow_widget::select_item(flow_widget_item* item)
|
void flow_widget::select_items(const std::set<flow_widget_item*>& selected_items, flow_widget_item* current_item)
|
||||||
{
|
{
|
||||||
const flow_layout::position selected_pos = find_item(item);
|
m_selected_items.clear();
|
||||||
const s64 selected_index = find_item(selected_pos);
|
|
||||||
|
|
||||||
if (selected_index < 0 || static_cast<usz>(selected_index) >= items().size())
|
for (flow_widget_item* item : selected_items)
|
||||||
{
|
{
|
||||||
m_selected_index = -1;
|
const flow_layout::position selected_pos = find_item(item);
|
||||||
return;
|
const s64 selected_index = find_item(selected_pos);
|
||||||
}
|
|
||||||
|
|
||||||
m_selected_index = selected_index;
|
if (selected_index < 0 || static_cast<usz>(selected_index) >= items().size())
|
||||||
Q_EMIT ItemSelectionChanged(m_selected_index);
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_selected_items.insert(selected_index);
|
||||||
|
|
||||||
|
if (!m_allow_multi_selection)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (usz i = 0; i < items().size(); i++)
|
for (usz i = 0; i < items().size(); i++)
|
||||||
{
|
{
|
||||||
if (flow_widget_item* item = items().at(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.
|
// We need to polish the widgets in order to re-apply any stylesheet changes for the selected property.
|
||||||
item->selected = m_selected_index >= 0 && i == static_cast<usz>(m_selected_index);
|
item->selected = m_selected_items.contains(i);
|
||||||
item->polish_style();
|
item->polish_style();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we see the focused widget
|
if (m_selected_items.empty())
|
||||||
m_scroll_area->ensureWidgetVisible(::at32(items(), m_selected_index));
|
{
|
||||||
|
m_last_selected_item = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!m_selected_items.contains(m_last_selected_item))
|
||||||
|
{
|
||||||
|
m_last_selected_item = *m_selected_items.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
s64 selected_item = m_last_selected_item;
|
||||||
|
s64 focused_item = selected_item;
|
||||||
|
|
||||||
|
if (current_item)
|
||||||
|
{
|
||||||
|
if (const flow_layout::position selected_pos = find_item(current_item))
|
||||||
|
{
|
||||||
|
focused_item = find_item(selected_pos);
|
||||||
|
|
||||||
|
if (current_item->selected)
|
||||||
|
{
|
||||||
|
selected_item = focused_item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we see the focused widget
|
||||||
|
m_scroll_area->ensureWidgetVisible(::at32(items(), focused_item));
|
||||||
|
|
||||||
|
Q_EMIT ItemSelectionChanged(selected_item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void flow_widget::update_selection(flow_widget_item* current_item)
|
||||||
|
{
|
||||||
|
std::set<flow_widget_item*> selected_items;
|
||||||
|
const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
|
||||||
|
|
||||||
|
if (current_item)
|
||||||
|
{
|
||||||
|
if (!m_allow_multi_selection || !current_item->selected || modifiers != Qt::ControlModifier)
|
||||||
|
{
|
||||||
|
selected_items.insert(current_item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_allow_multi_selection)
|
||||||
|
{
|
||||||
|
flow_layout::position selected_pos;
|
||||||
|
flow_layout::position last_selected_pos;
|
||||||
|
|
||||||
|
if (modifiers == Qt::ShiftModifier && m_last_selected_item >= 0 && static_cast<usz>(m_last_selected_item) < items().size())
|
||||||
|
{
|
||||||
|
selected_pos = find_item(current_item);
|
||||||
|
last_selected_pos = find_item(::at32(m_widgets, m_last_selected_item));
|
||||||
|
}
|
||||||
|
|
||||||
|
flow_layout::position pos_min = last_selected_pos;
|
||||||
|
flow_layout::position pos_max = selected_pos;
|
||||||
|
|
||||||
|
if (pos_min.row > pos_max.row)
|
||||||
|
{
|
||||||
|
std::swap(pos_min, pos_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the item is between the last and the current selection
|
||||||
|
const auto item_between = [this, &pos_min, &pos_max](flow_widget_item* item)
|
||||||
|
{
|
||||||
|
if (!pos_min || !pos_max) return false;
|
||||||
|
|
||||||
|
const flow_layout::position pos = find_item(item);
|
||||||
|
if (!pos) return false; // pos invalid
|
||||||
|
if (pos.row < pos_min.row || pos.row > pos_max.row) return false; // not in any relevant row
|
||||||
|
if (pos.row > pos_min.row && pos.row < pos_max.row) return true; // in a row between the items -> match
|
||||||
|
|
||||||
|
const bool in_min_row = pos.row == pos_min.row && pos.col >= pos_min.col;
|
||||||
|
const bool in_max_row = pos.row == pos_max.row && pos.col <= pos_max.col;
|
||||||
|
|
||||||
|
if (pos_min.row == pos_max.row) return in_min_row && in_max_row; // in the only row at a relevant col -> match
|
||||||
|
|
||||||
|
return in_min_row || in_max_row; // in either min or max row at a relevant col -> match
|
||||||
|
};
|
||||||
|
|
||||||
|
for (usz i = 0; i < items().size(); i++)
|
||||||
|
{
|
||||||
|
if (flow_widget_item* item = items().at(i))
|
||||||
|
{
|
||||||
|
if (item == current_item) continue;
|
||||||
|
|
||||||
|
if (modifiers == Qt::ControlModifier && item->selected)
|
||||||
|
{
|
||||||
|
selected_items.insert(item);
|
||||||
|
}
|
||||||
|
else if (modifiers == Qt::ShiftModifier && item_between(item))
|
||||||
|
{
|
||||||
|
selected_items.insert(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select_items(selected_items, current_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
void flow_widget::on_item_focus()
|
void flow_widget::on_item_focus()
|
||||||
{
|
{
|
||||||
select_item(static_cast<flow_widget_item*>(QObject::sender()));
|
update_selection(static_cast<flow_widget_item*>(QObject::sender()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void flow_widget::on_navigate(flow_navigation value)
|
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 flow_layout::position selected_pos = find_next_item(find_item(static_cast<flow_widget_item*>(QObject::sender())), value);
|
||||||
const s64 selected_index = find_item(selected_pos);
|
const s64 selected_index = find_item(selected_pos);
|
||||||
if (selected_index < 0 || static_cast<usz>(selected_index) >= items().size())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flow_widget_item* item = items().at(selected_index))
|
if (selected_index >= 0 && static_cast<usz>(selected_index) < items().size())
|
||||||
{
|
{
|
||||||
item->setFocus();
|
if (flow_widget_item* item = items().at(selected_index))
|
||||||
|
{
|
||||||
|
item->setFocus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_selected_index = selected_index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void flow_widget::mouseDoubleClickEvent(QMouseEvent* ev)
|
void flow_widget::mouseDoubleClickEvent(QMouseEvent* ev)
|
||||||
|
|||||||
@ -19,8 +19,10 @@ public:
|
|||||||
void add_widget(flow_widget_item* widget);
|
void add_widget(flow_widget_item* widget);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
void set_multi_selection_enabled(bool enabled) { m_allow_multi_selection = enabled; }
|
||||||
|
|
||||||
std::vector<flow_widget_item*>& items() { return m_widgets; }
|
std::vector<flow_widget_item*>& items() { return m_widgets; }
|
||||||
flow_widget_item* selected_item() const;
|
std::set<flow_widget_item*> selected_items() const;
|
||||||
QScrollArea* scroll_area() const { return m_scroll_area; }
|
QScrollArea* scroll_area() const { return m_scroll_area; }
|
||||||
|
|
||||||
void paintEvent(QPaintEvent* event) override;
|
void paintEvent(QPaintEvent* event) override;
|
||||||
@ -33,7 +35,7 @@ private Q_SLOTS:
|
|||||||
void on_navigate(flow_navigation value);
|
void on_navigate(flow_navigation value);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void select_item(flow_widget_item* item);
|
void select_items(const std::set<flow_widget_item*>& selected_items, flow_widget_item* current_item = nullptr);
|
||||||
void mouseDoubleClickEvent(QMouseEvent* event) override;
|
void mouseDoubleClickEvent(QMouseEvent* event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -41,8 +43,12 @@ private:
|
|||||||
flow_layout::position find_item(flow_widget_item* item);
|
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::position find_next_item(flow_layout::position current_pos, flow_navigation value);
|
||||||
|
|
||||||
|
void update_selection(flow_widget_item* current_item);
|
||||||
|
|
||||||
flow_layout* m_flow_layout{};
|
flow_layout* m_flow_layout{};
|
||||||
QScrollArea* m_scroll_area{};
|
QScrollArea* m_scroll_area{};
|
||||||
std::vector<flow_widget_item*> m_widgets;
|
std::vector<flow_widget_item*> m_widgets;
|
||||||
s64 m_selected_index = -1;
|
std::set<s64> m_selected_items;
|
||||||
|
s64 m_last_selected_item = -1;
|
||||||
|
bool m_allow_multi_selection = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -961,32 +961,31 @@ void game_list_frame::ShowContextMenu(const QPoint& pos)
|
|||||||
QPoint global_pos;
|
QPoint global_pos;
|
||||||
std::vector<game_info> games;
|
std::vector<game_info> games;
|
||||||
|
|
||||||
// NOTE: Currently, only m_game_list supports rows multi selection!
|
|
||||||
//
|
|
||||||
// TODO: Add support to rows multi selection to m_game_grid
|
|
||||||
|
|
||||||
if (m_is_list_layout)
|
if (m_is_list_layout)
|
||||||
{
|
{
|
||||||
global_pos = m_game_list->viewport()->mapToGlobal(pos);
|
global_pos = m_game_list->viewport()->mapToGlobal(pos);
|
||||||
|
|
||||||
const auto item_list = m_game_list->selectedItems();
|
for (const QTableWidgetItem* item : m_game_list->selectedItems())
|
||||||
game_info gameinfo;
|
|
||||||
|
|
||||||
for (const auto& item : item_list)
|
|
||||||
{
|
{
|
||||||
if (item->column() != static_cast<int>(gui::game_list_columns::icon))
|
if (!item || item->column() != static_cast<int>(gui::game_list_columns::icon))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (gameinfo = GetGameInfoFromItem(item); gameinfo)
|
if (game_info gameinfo = GetGameInfoFromItem(item))
|
||||||
games.push_back(gameinfo);
|
games.push_back(gameinfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (game_list_grid_item* item = static_cast<game_list_grid_item*>(m_game_grid->selected_item()))
|
else if (m_game_grid)
|
||||||
{
|
{
|
||||||
global_pos = m_game_grid->mapToGlobal(pos);
|
global_pos = m_game_grid->mapToGlobal(pos);
|
||||||
|
|
||||||
if (game_info gameinfo = item->game(); gameinfo)
|
for (const flow_widget_item* selected_item : m_game_grid->selected_items())
|
||||||
games.push_back(gameinfo);
|
{
|
||||||
|
if (const game_list_grid_item* item = static_cast<const game_list_grid_item*>(selected_item))
|
||||||
|
{
|
||||||
|
if (game_info gameinfo = item->game())
|
||||||
|
games.push_back(gameinfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!games.empty())
|
if (!games.empty())
|
||||||
@ -1154,16 +1153,19 @@ bool game_list_frame::eventFilter(QObject *object, QEvent *event)
|
|||||||
|
|
||||||
if (object == m_game_list)
|
if (object == m_game_list)
|
||||||
{
|
{
|
||||||
QTableWidgetItem* item = m_game_list->item(m_game_list->currentRow(), static_cast<int>(gui::game_list_columns::icon));
|
const QTableWidgetItem* item = m_game_list->item(m_game_list->currentRow(), static_cast<int>(gui::game_list_columns::icon));
|
||||||
|
|
||||||
if (!item || !item->isSelected())
|
if (item && item->isSelected())
|
||||||
return false;
|
{
|
||||||
|
gameinfo = GetGameInfoFromItem(item);
|
||||||
gameinfo = GetGameInfoFromItem(item);
|
}
|
||||||
}
|
}
|
||||||
else if (game_list_grid_item* item = static_cast<game_list_grid_item*>(m_game_grid->selected_item()))
|
else if (const auto items = m_game_grid->selected_items(); !items.empty())
|
||||||
{
|
{
|
||||||
gameinfo = item->game();
|
if (const game_list_grid_item* item = static_cast<const game_list_grid_item*>(*items.begin()))
|
||||||
|
{
|
||||||
|
gameinfo = item->game();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gameinfo)
|
if (!gameinfo)
|
||||||
@ -1279,11 +1281,14 @@ std::set<std::string> game_list_frame::CurrentSelectionPaths()
|
|||||||
}
|
}
|
||||||
else if (m_game_grid)
|
else if (m_game_grid)
|
||||||
{
|
{
|
||||||
if (const game_list_grid_item* item = static_cast<game_list_grid_item*>(m_game_grid->selected_item()))
|
for (const flow_widget_item* selected_item : m_game_grid->selected_items())
|
||||||
{
|
{
|
||||||
if (const game_info& game = item->game())
|
if (const game_list_grid_item* item = static_cast<const game_list_grid_item*>(selected_item))
|
||||||
{
|
{
|
||||||
selection.insert(game->info.path + game->info.icon_path);
|
if (const game_info& game = item->game())
|
||||||
|
{
|
||||||
|
selection.insert(game->info.path + game->info.icon_path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,8 @@ game_list_grid::game_list_grid()
|
|||||||
setObjectName("game_list_grid");
|
setObjectName("game_list_grid");
|
||||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
|
||||||
|
set_multi_selection_enabled(true);
|
||||||
|
|
||||||
m_icon_ready_callback = [this](const game_info& game, const movie_item_base* item)
|
m_icon_ready_callback = [this](const game_info& game, const movie_item_base* item)
|
||||||
{
|
{
|
||||||
Q_EMIT IconReady(game, item);
|
Q_EMIT IconReady(game, item);
|
||||||
@ -45,7 +47,7 @@ void game_list_grid::populate(
|
|||||||
{
|
{
|
||||||
clear_list();
|
clear_list();
|
||||||
|
|
||||||
game_list_grid_item* selected_item = nullptr;
|
std::set<flow_widget_item*> selected_items;
|
||||||
|
|
||||||
blockSignals(true);
|
blockSignals(true);
|
||||||
|
|
||||||
@ -112,7 +114,7 @@ void game_list_grid::populate(
|
|||||||
|
|
||||||
if (selected_item_ids.contains(game->info.path + game->info.icon_path))
|
if (selected_item_ids.contains(game->info.path + game->info.icon_path))
|
||||||
{
|
{
|
||||||
selected_item = item;
|
selected_items.insert(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
add_widget(item);
|
add_widget(item);
|
||||||
@ -125,7 +127,7 @@ void game_list_grid::populate(
|
|||||||
|
|
||||||
QApplication::processEvents();
|
QApplication::processEvents();
|
||||||
|
|
||||||
select_item(selected_item);
|
select_items(selected_items);
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_list_grid::repaint_icons(std::vector<game_info>& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio)
|
void game_list_grid::repaint_icons(std::vector<game_info>& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user