#include "stdafx.h" #include "flow_widget.h" #include #include #include #include flow_widget::flow_widget(QWidget* parent) : QWidget(parent) { m_flow_layout = new flow_layout(); QWidget* widget = new QWidget(this); widget->setLayout(m_flow_layout); widget->setObjectName("flow_widget_content"); widget->setFocusProxy(this); m_scroll_area = new QScrollArea(this); m_scroll_area->setWidget(widget); m_scroll_area->setWidgetResizable(true); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_scroll_area); layout->setContentsMargins(0, 0, 0, 0); setLayout(layout); } flow_widget::~flow_widget() { } void flow_widget::add_widget(flow_widget_item* widget) { if (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); } } void flow_widget::clear() { m_widgets.clear(); m_flow_layout->clear(); } QList& flow_widget::items() { return m_widgets; } flow_widget_item* flow_widget::selected_item() const { if (m_selected_index >= 0 && m_selected_index < m_widgets.size()) { 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) { 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) { return ::at32(positions, i); } } } return flow_layout::position{ .row = -1, .col = - 1}; } flow_layout::position flow_widget::find_next_item(flow_layout::position current_pos, flow_navigation value) { if (current_pos.row >= 0 && current_pos.col >= 0 && m_flow_layout->rows() > 0 && m_flow_layout->cols() > 0) { switch (value) { case flow_navigation::up: // Go up one row. if (current_pos.row > 0) { current_pos.row--; } break; case flow_navigation::down: // Go down one row. Beware of last row which might have less columns. for (const auto& pos : m_flow_layout->positions()) {; if (pos.col != current_pos.col) continue; if (pos.row == current_pos.row + 1) { current_pos.row = pos.row; break; } } break; case flow_navigation::left: // Go left one column. if (current_pos.col > 0) { current_pos.col--; } break; case flow_navigation::right: // Go right one column. Beware of last row which might have less columns. for (const auto& pos : m_flow_layout->positions()) { if (pos.row > current_pos.row) break; if (pos.row < current_pos.row) continue; if (pos.col == current_pos.col + 1) { current_pos.col = pos.col; break; } } break; case flow_navigation::home: // Go to leftmost column. current_pos.col = 0; break; case flow_navigation::end: // Go to last column. Beware of last row which might have less columns. for (const auto& pos : m_flow_layout->positions()) { if (pos.row > current_pos.row) break; if (pos.row < current_pos.row) continue; current_pos.col = std::max(current_pos.col, pos.col); } break; case flow_navigation::page_up: // Go to top row. current_pos.row = 0; break; case flow_navigation::page_down: // Go to bottom row. Beware of last row which might have less columns. for (const auto& pos : m_flow_layout->positions()) { if (pos.col != current_pos.col) continue; current_pos.row = std::max(current_pos.row, pos.row); } break; } } return current_pos; } void flow_widget::select_item(flow_widget_item* item) { const flow_layout::position selected_pos = find_item(item); const int selected_index = find_item(selected_pos); if (selected_index < 0 || selected_index >= items().size()) { m_selected_index = -1; return; } m_selected_index = selected_index; Q_EMIT ItemSelectionChanged(m_selected_index); for (int i = 0; i < items().size(); i++) { if (flow_widget_item* item = items().at(i)) { // We need to polish the widgets in order to re-apply any stylesheet changes for the selected property. item->selected = i == m_selected_index; item->polish_style(); } } // Make sure we see the focused widget m_scroll_area->ensureWidgetVisible(::at32(items(), m_selected_index)); } void flow_widget::on_item_focus() { select_item(static_cast(QObject::sender())); } void flow_widget::on_navigate(flow_navigation value) { const flow_layout::position selected_pos = find_next_item(find_item(static_cast(QObject::sender())), value); const int selected_index = find_item(selected_pos); if (selected_index < 0 || selected_index >= items().size()) { return; } if (flow_widget_item* item = items().at(selected_index)) { item->setFocus(); } m_selected_index = selected_index; }