Program Listing for File sectiondrawingarea.cpp

Return to documentation for file (src/drawingarea/sectiondrawingarea.cpp)

#include <QPainter>
#include <QSettings>
#include "sectiondrawingarea.h"
#include "dialog/preferencesdialog.h"

namespace PixelMaestroStudio {
    SectionDrawingArea::SectionDrawingArea(QWidget* parent, Section& section, uint8_t section_id) : QFrame(parent), maestro_drawing_area_(*dynamic_cast<MaestroDrawingArea*>(parent)), section_(section) {
        this->section_id_ = section_id;

        // Enable mouse tracking
        this->setMouseTracking(true);

        // Display a frame if none is set and default to inactive.
        if (this->frameStyle() == QFrame::Plain) {
            this->setFrameStyle(QFrame::Box | QFrame::Plain);
            this->draw_frame(FrameType::Inactive);
        }

        QSettings settings;
        this->pixel_shape_ = settings.value(PreferencesDialog::pixel_shape, 1).toInt();
    }

    /*
     * If this is the active Section, highlight the frame, otherwise dim the frame.
     * Only applies if maestro_drawing_area_::maestro_control_widget_ is set.
     */
    void SectionDrawingArea::draw_frame(FrameType type) {
        /*
         * Set the frame color.
         * The current Section has a white frame, Layers have a light gray frame, and inactive Sections have dark frames.
         */
        switch (type) {
            case FrameType::Inactive:
                this->setStyleSheet("color: #808080;");
                break;
            case FrameType::Active:
                this->setStyleSheet("color: #FFFFFF;");
                break;
        }
    }

    Section& SectionDrawingArea::get_section() const {
        return this->section_;
    }

    Point SectionDrawingArea::map_cursor_to_pixel(const QPoint cursor) {
        uint16_t x = cursor.x() - section_cursor_.x;
        uint16_t y = cursor.y() - section_cursor_.y;

        if (radius_ > 0) {
            x /= radius_;
            y /= radius_;
            if (x >= section_.get_dimensions().x) {
                x = section_.get_dimensions().x - 1;
            }
            if (y >= section_.get_dimensions().y) {
                y = section_.get_dimensions().y - 1;
            }

            return Point(x, y);
        }
        else {
            return Point(0, 0);
        }
    }

    void SectionDrawingArea::mouseMoveEvent(QMouseEvent *event) {
        // Store the cursor position for Canvas editing
        cursor_pos_ = event->pos();

        if (event->buttons() == Qt::LeftButton || event->buttons() == Qt::RightButton) {
            Canvas* canvas = section_.get_canvas();
            if (canvas != nullptr) {
                Point pixel = map_cursor_to_pixel(cursor_pos_);

                // If there's a MaestroControlWidget, use run_cue instead of modifying the Canvas directly.
                MaestroControlWidget* widget = maestro_drawing_area_.get_maestro_control_widget();
                if (widget != nullptr) {
                    // Set the cursor location in the MaestroControlWidget
                    widget->canvas_control_widget_->set_canvas_origin(pixel);

                    // Check to see if paint mode is enabled.
                    if (widget->canvas_control_widget_->get_painting_enabled()) {
                        if (event->buttons() == Qt::LeftButton) {
                            widget->run_cue(
                                widget->canvas_handler->draw_point(
                                    widget->section_control_widget_->get_section_index(),
                                    widget->section_control_widget_->get_layer_index(),
                                    canvas->get_current_frame_index(),
                                    widget->canvas_control_widget_->get_selected_color_index(),
                                    pixel.x,
                                    pixel.y)
                            );
                        }
                        else if (event->buttons() == Qt::RightButton) {
                            widget->run_cue(
                                widget->canvas_handler->erase_point(
                                    widget->section_control_widget_->get_section_index(),
                                    widget->section_control_widget_->get_layer_index(),
                                    canvas->get_current_frame_index(),
                                    pixel.x,
                                    pixel.y)
                            );
                        }
                    }

                    if (widget->canvas_control_widget_->get_replace_enabled()) {
                        if (event->buttons() == Qt::LeftButton) {
                            widget->canvas_control_widget_->on_drawButton_clicked();
                        }
                    }
                }
            }
        }
    }

    void SectionDrawingArea::mousePressEvent(QMouseEvent *event) {
        // Sets the current Section as the active Section on left click
        if (event->buttons() == Qt::LeftButton) {
            Section& active_section = maestro_drawing_area_.get_maestro_control_widget()->section_control_widget_->get_active_section();
            if (&active_section != &this->section_) {
                maestro_drawing_area_.get_maestro_control_widget()->section_control_widget_->set_active_section(&this->section_);
            }
        }

        mouseMoveEvent(event);
    }

    void SectionDrawingArea::paintEvent(QPaintEvent *event) {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);

        /*
         * Check to see if the Section's changed sizes.
         * If so, recalculate the drawing area's dimensions.
         */
        if (last_pixel_count_ != section_.get_dimensions().size()) {
            resizeEvent(nullptr);
            last_pixel_count_ = section_.get_dimensions().size();
        }

        /*
         * Draw each Pixel.
         * For each Pixel, translate it's RGB color into a QColor.
         * Then, depending on the user's preferenes, draw it as either a circle or a square.
         */
        for (uint16_t row = 0; row < section_.get_dimensions().y; row++) {
            for (uint16_t column = 0; column < section_.get_dimensions().x; column++) {
                Colors::RGB rgb = section_.get_pixel_color(column, row);
                QColor qcolor;
                QBrush brush;
                QRect rect;

                qcolor.setRgb(rgb.r, rgb.g, rgb.b);
                brush.setColor(qcolor);
                brush.setStyle(Qt::BrushStyle::SolidPattern);

                /*
                 * Draw the Pixel.
                 * First, calculate the bounds of the Pixel.
                 * Then, set the color of the pen to the color of the Pixel.
                 * Finally, draw the Pixel to the screen.
                 */
                rect.setRect(section_cursor_.x + (column * radius_), section_cursor_.y + (row * radius_), radius_, radius_);
                painter.setBrush(brush);

                // Set Pen style.
                // If Canvas is enabled, draw a light border around the Pixel if the cursor is over it
                painter.setPen(Qt::PenStyle::NoPen);
                if (section_.get_canvas() != nullptr) {
                    Point pixel_pos = map_cursor_to_pixel(cursor_pos_);
                    if (pixel_pos.x == column && pixel_pos.y == row) {
                        painter.setPen(Qt::PenStyle::SolidLine);
                    }
                }

                /*
                 * Determine which shape to draw.
                 * If none is set, default to "Square"
                 */
                switch (pixel_shape_) {
                    case 0: // Circle
                        painter.drawEllipse(rect);
                        break;
                    case 1: // Rect
                        painter.drawRect(rect);
                        break;
                }
            }
        }

        QFrame::paintEvent(event);
    }

    void SectionDrawingArea::resizeEvent(QResizeEvent *event) {
        QSize widget_size = this->size();

        // Next, get the max size of each Pixel via the window size.
        uint16_t max_pixel_width = static_cast<uint16_t>(widget_size.width() / section_.get_dimensions().x);
        uint16_t max_pixel_height = static_cast<uint16_t>(widget_size.height() / section_.get_dimensions().y);

        // Find the smaller dimension
        if (max_pixel_width < max_pixel_height) {
            radius_ = max_pixel_width;
        }
        else {
            radius_ = max_pixel_height;
        }

        // Sets the Section's starting point so that it's aligned horizontally and vertically.
        section_cursor_.x = (widget_size.width() - (section_.get_dimensions().x * radius_)) / 2;
        section_cursor_.y = (widget_size.height() - (section_.get_dimensions().y * radius_)) / 2;
    }
}