mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-04-07 23:45:12 +00:00
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:
parent
0b628cb50e
commit
f115032095
48 changed files with 2563 additions and 1601 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue