Commit 88a33280 authored by Yann Boucher's avatar Yann Boucher
Browse files

Added base implementation of the Grid Widget

parent cf58a96e
Pipeline #77338 passed with stages
in 16 seconds
......@@ -43,4 +43,23 @@ inline bool operator<(const Coord& lhs, const Coord& rhs)
return lhs.x < rhs.x;
}
//! \brief Addition de coordonnées
inline Coord operator+(const Coord& lhs, const Coord& rhs)
{
return {lhs.x + rhs.x, lhs.y + rhs.y};
}
//! \brief Soustraction de coordonnées
inline Coord operator-(const Coord& lhs, const Coord& rhs)
{
return {lhs.x - rhs.x, lhs.y - rhs.y};
}
//! \brief Retourne une coordonnée ajustée par rapport à un réseau périodique de taille (cols, rows)
inline Coord wrap_coords(const Coord& coord, unsigned cols, unsigned rows)
{
return Coord{ (int)((coord.x%cols+cols)%cols),
(int)((coord.y%rows + rows)%rows) };
}
#endif // COORD_HPP
......@@ -19,6 +19,8 @@ Cette classe représente un réseau de cellules.
using namespace std;
// TODO : documenter
class Grid{
int nb_rows;
int nb_col;
......
......@@ -11,6 +11,10 @@ Cette classe représente le widget utilisé pour l'affichage et l'interaction av
#ifndef GRIDVIEW_HPP
#define GRIDVIEW_HPP
// TODO : documenter
#include <map>
#include <QFrame>
#include <QGraphicsView>
......@@ -21,6 +25,7 @@ Cette classe représente le widget utilisé pour l'affichage et l'interaction av
#include "grid.h"
class GridItem;
class GridView : public QFrame
{
Q_OBJECT
......@@ -34,6 +39,14 @@ public:
void clear_selection();
Structure selected_cells() const;
private:
GridItem* item_at(Coord pos);
void copy_selection();
void paste_clipboard();
void delete_selection();
Coord top_left_of_selection() const;
protected:
void keyPressEvent(QKeyEvent *event);
void keyReleaseEvent(QKeyEvent *event);
......@@ -41,6 +54,7 @@ protected:
private:
QGraphicsView* m_view;
QGraphicsScene* m_scene;
Structure m_copy_paste_buffer;
};
......
......@@ -59,6 +59,11 @@ public:
m_cells[it->first] = it->second;
}
}
//! \brief Fonction vidant la structure, la laissant vide.
void clear()
{
m_cells.clear();
}
//! \brief retourne le nombre de cellules présentes dans la structure
size_t size() const
......
......@@ -21,13 +21,15 @@ Cette classe représente le widget utilisé pour l'affichage et l'interaction av
#include <QDebug>
#include <vector>
#include <qmath.h>
class GridItem : public QGraphicsRectItem
{
public:
GridItem(QColor color, const QString& state_name, QGraphicsItem *parent = nullptr)
: QGraphicsRectItem(0, 0, 20, 20, parent), m_state_name(state_name)
: QGraphicsRectItem(0, 0, GridItem::width(), GridItem::height(), parent), m_state_name(state_name)
{
setAcceptHoverEvents(true);
setPen(QPen(QBrush(color), 0));
......@@ -36,6 +38,11 @@ public:
setFlag(QGraphicsItem::ItemIsSelectable);
}
static int width()
{ return 20; }
static int height()
{ return 20; }
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
......@@ -76,7 +83,7 @@ GridView::GridView(QWidget *parent)
//m_scene->setBackgroundBrush(Qt::black);
m_view = new QGraphicsView(this);
m_view->setRenderHint(QPainter::Antialiasing, true);
m_view->setRenderHint(QPainter::Antialiasing, false);
m_view->setDragMode(QGraphicsView::RubberBandDrag);
m_view->setOptimizationFlags(QGraphicsView::DontSavePainterState);
m_view->setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
......@@ -101,8 +108,10 @@ GridView::GridView(QWidget *parent)
{
QColor color(Qt::black);
QGraphicsRectItem *item = new GridItem((i ^ j) % 2 ? Qt::white : Qt::blue, (i ^ j) % 2 ? "Alive" : "Dead");
item->setPos(QPointF(i*20, j*20));
GridItem *item = new GridItem((i ^ j) % 2 ? Qt::blue : Qt::white, (i ^ j) % 2 ? "Alive" : "Dead");
item->setPos(QPointF(i*GridItem::width(), j*GridItem::height()));
item->cell_pos = Coord{(int)i, (int)j};
item->cell_state = (i ^ j) % 2;
m_scene->addItem(item);
}
}
......@@ -119,8 +128,10 @@ void GridView::copy_grid(const Grid &grid)
QColor color(Qt::black);
unsigned state = grid.get_state(j, i);
// TODO : récupérer state color
QGraphicsRectItem *item = new GridItem((i ^ j) % 2 ? Qt::white : Qt::blue, (i ^ j) % 2 ? "Alive" : "Dead");
item->setPos(QPointF(i*20, j*20));
GridItem *item = new GridItem((i ^ j) % 2 ? Qt::blue : Qt::white, (i ^ j) % 2 ? "Alive" : "Dead");
item->setPos(QPointF(i*GridItem::width(), j*GridItem::height()));
item->cell_pos = Coord{(int)i, (int)j};
item->cell_state = state;
m_scene->addItem(item);
}
}
......@@ -139,21 +150,119 @@ void GridView::clear_selection()
Structure GridView::selected_cells() const
{
if (m_scene->selectedItems().empty())
return Structure{};
std::vector<std::pair<Coord, unsigned>> cells;
// on calcule la position du coin supérieur gauche de la sélection, afin de la considérer comme l'origine de de la structure
Coord origin = top_left_of_selection();
for (const auto& item : m_scene->selectedItems())
{
GridItem* cell = static_cast<GridItem*>(item);
cells.push_back(std::make_pair(cell->cell_pos, cell->cell_state));
cells.push_back(std::make_pair(cell->cell_pos - origin, cell->cell_state));
}
return Structure(cells.begin(), cells.end());
}
GridItem *GridView::item_at(Coord pos)
{
QGraphicsItem* gitem_ptr = m_scene->itemAt(QPointF(pos.x*GridItem::width(), pos.y*GridItem::height()), QTransform());
return dynamic_cast<GridItem*>(gitem_ptr);
}
void GridView::copy_selection()
{
m_copy_paste_buffer = selected_cells();
}
void GridView::paste_clipboard()
{
// la destination est la cellule sélectionnée actuelle, on ne fait rien si on est pas dans ce cas
if (m_scene->selectedItems().size() < 1)
return;
Coord origin = top_left_of_selection();
for (const auto& cell : m_copy_paste_buffer)
{
Coord corrected = wrap_coords(cell.first + origin, 100, 100);
GridItem* item = item_at(corrected);
assert(item != nullptr);
item->cell_state = cell.second;
item->setBrush(QBrush(cell.second ? Qt::blue : Qt::white));
item->update();
}
}
void GridView::delete_selection()
{
for (auto& abstract_item : m_scene->selectedItems())
{
GridItem* item = static_cast<GridItem*>(abstract_item);
item->setBrush(QBrush(Qt::white));
item->cell_state = 0;
item->update();
}
clear_selection();
}
Coord GridView::top_left_of_selection() const
{
// on calcule la position du coin supérieur gauche de la sélection
Coord selection_top_left = Coord{std::numeric_limits<int>::max(), std::numeric_limits<int>::max()};
for (const auto& item : m_scene->selectedItems())
{
GridItem* cell = static_cast<GridItem*>(item);
if (cell->cell_pos.x < selection_top_left.x)
selection_top_left.x = cell->cell_pos.x;
if (cell->cell_pos.y < selection_top_left.y)
selection_top_left.y = cell->cell_pos.y;
}
return selection_top_left;
}
void GridView::keyPressEvent(QKeyEvent *event)
{
if (event->modifiers() & Qt::ShiftModifier)
{
// prevent items from stealing the mouse event
for (auto& item : m_scene->items())
item->setAcceptedMouseButtons(Qt::NoButton);
m_view->setDragMode(QGraphicsView::ScrollHandDrag);
}
// copier-couper-coller
if (event->modifiers() & Qt::ControlModifier)
{
if (event->key() == Qt::Key_C)
{
copy_selection();
}
else if (event->key() == Qt::Key_X)
{
copy_selection();
delete_selection();
}
else if (event->key() == Qt::Key_V)
{
paste_clipboard();
}
}
// suppression de sélection
if (event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete)
{
delete_selection();
}
return QFrame::keyPressEvent(event);
}
void GridView::keyReleaseEvent(QKeyEvent *event)
......@@ -162,8 +271,12 @@ void GridView::keyReleaseEvent(QKeyEvent *event)
if (QGuiApplication::keyboardModifiers() & Qt::ShiftModifier)
{
m_view->setDragMode(QGraphicsView::RubberBandDrag);
// prevent item mouse event handling
for (auto& item : m_scene->items())
item->setAcceptedMouseButtons(Qt::AllButtons);
}
return QFrame::keyReleaseEvent(event);
}
detail::Graphics_view_zoom::Graphics_view_zoom(QGraphicsView* view)
......
......@@ -18,5 +18,9 @@ void CellulutTests::test_coord()
QVERIFY(b == b);
QVERIFY(a != b);
QVERIFY(!(a == b));
QCOMPARE(wrap_coords(Coord{3, 4}, 2, 2), (Coord{1, 0}));
QCOMPARE((Coord{1, 2} + Coord{3, 4}), (Coord{4, 6}));
QCOMPARE((Coord{1, 2} - Coord{3, 4}), (Coord{-2, -2}));
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment