Интерфейс инструмента:
#pragma once
struct point {
int x;
int y;
};
enum EditorTool {
TOOL_PENCIL, TOOL_BRUSH, TOOL_LINE, TOOL_ERASER, TOOL_FILL, TOOL_PIPETTE
};
ref class Tool abstract {
protected:
static System::Drawing::Color color = System::Drawing::Color::Black;
static System::Drawing::Pen ^colored_pen = gcnew System::Drawing::Pen(color);
static System::Drawing::Pen ^colored_thick_pen = gcnew System::Drawing::Pen(color, (float)30);
static System::Drawing::Brush ^colored_brush = gcnew System::Drawing::SolidBrush(color);
static unsigned thickness = 30;
public:
Tool() { }
virtual ~Tool() { }
static System::Drawing::Color GetColor() {
return color;
}
static void SetColor(System::Drawing::Color _color) {
color = _color;
colored_pen->Color = color;
colored_thick_pen->Color = color;
colored_brush = gcnew System::Drawing::SolidBrush(color);
ColorChanged(nullptr, nullptr);
}
static System::Drawing::Pen ^GetPen() {
return colored_pen;
}
static System::Drawing::Brush ^GetBrush() {
return colored_brush;
}
static unsigned GetThickness() {
return thickness;
}
static void SetThickness(unsigned _thickness) {
thickness = _thickness;
colored_thick_pen->Width = (float)thickness;
ThicknessChanged(nullptr, nullptr);
}
static event System::EventHandler ^ColorChanged;
static event System::EventHandler ^ThicknessChanged;
virtual EditorTool Identifier() = 0;
// true если рисунок изменён этим инструментом
// false если нет (например, пипетка или выделение не изменяют рисунок)
virtual bool IsMutating() = 0;
// true если изменение инструмента постоянно (напр., карандаш рисует все движения)
// false если нет (например, линия рисуется только после отпускания, а до этого предпросмотр)
// ВНИМАНИЕ: ставить в false ТОЛЬКО если инструмент ТРЕБУЕТ предпросмотра!
virtual bool IsMovementPermanent() = 0;
// true если использует ширину
// false если ширина неприменима к инструменту
virtual bool IsThickable() = 0;
// срабатывает при нажатии мыши
virtual void BeginDraw(System::Drawing::Bitmap ^bmp, System::Drawing::Point ^location) = 0;
// срабатывает при ведении мыши
virtual void ContinueDraw(System::Drawing::Graphics ^g, System::Drawing::Point ^location) = 0;
// срабатывает при отпускании мыши
virtual void EndDraw(System::Drawing::Graphics ^g, System::Drawing::Point ^location) = 0;
};
Основные действия, доступные из меню
|
void New() {
if (!ConfirmForLosingChanges()) return;
isFileEdited = false;
isDrawingNow = false;
needInvalidation = true; // обновление в самом начале, чтобы показать пустой рисунок
assignedFileName = String::Empty;
real_image = gcnew Bitmap(400, 300, System::Drawing::Imaging::PixelFormat::Format32bppArgb);
g = Graphics::FromImage(real_image);
// если нет непостоянных изменений, g_tmp == g
g_tmp = g;
// заливаем новую картинку белым цветом
g->FillRectangle(gcnew SolidBrush(System::Drawing::Color::White), 0, 0, real_image->Width, real_image->Height);
// обновляем экран
pictureBox1->Size = real_image->Size;
needInvalidation = true;
pictureBox1->Invalidate();
}
void Open() {
if (!ConfirmForLosingChanges()) return;
System::Windows::Forms::DialogResult ^dr = openFileDialog1->ShowDialog();
if(*dr == System::Windows::Forms::DialogResult::OK) {
New();
real_image = gcnew Bitmap(openFileDialog1->FileName);
g = Graphics::FromImage(real_image);
g_tmp = g;
assignedFileName = openFileDialog1->FileName;
// обновляем экран
pictureBox1->Size = real_image->Size;
needInvalidation = true;
pictureBox1->Invalidate();
}
}
// возвращает true если сохранение было совершено
// false - если нет, например, пользователь нажал "Отмена"
bool Save() {
if (assignedFileName->Equals(System::String::Empty)) {
return SaveAs();
}
real_image->Save(assignedFileName);
// помечаем файл как оригинальный (его только что сохранили, с тех пор изменений не было)
isFileEdited = false;
return true;
}
bool SaveAs() {
// спрашиваем имя файла
System::Windows::Forms::DialogResult ^dr = saveFileDialog1->ShowDialog();
if (*dr!= System::Windows::Forms::DialogResult::OK) return false;
assignedFileName = saveFileDialog1->FileName;
// вызываем функцию обычного сохранения, которая либо вернёт true, либо кинет исключение
Save();
return true;
}
void About() {
MessageBox::Show("Разработал: Тихонов В.\nГруппа: АТ-63\nНовосибирск, 2017", "О программе");
}
void Exit() {
if (!ConfirmForLosingChanges()) return;
Application::Exit();
}
// вспомогательные действия
// спрашиваем, готов ли пользователь продолжить с несохранённой картинкой.
|
// если false, отменяем вызвавшее действие
// если true - продолжаем (при этом пользователь всё сохранил, если захотел)
bool ConfirmForLosingChanges() {
if (isFileEdited) {
System::Windows::Forms::DialogResult ^dr =
MessageBox::Show(
"Файл изменён, сохранить изменения?"
, "Предупреждение"
, MessageBoxButtons::YesNoCancel
);
switch (*dr) {
case System::Windows::Forms::DialogResult::Yes:
// если пользователь нажал "да" а потом "отмена" - не завершать программу
if (!Save()) return false;
break;
case System::Windows::Forms::DialogResult::No:
// если пользователь ответил "нет" - больше не надоедать
isFileEdited = false;
break;
case System::Windows::Forms::DialogResult::Cancel:
// не завершать программу
return false;
break;
}
}
return true;
}
События рисования
private: System::Void pictureBox1_MouseDown(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) {
// только на левую кнопку
if (e->Button!= System::Windows::Forms::MouseButtons::Left) return;
System::Windows::Forms::Cursor ^prevCursor = this->Cursor;
this->Cursor = Cursors::WaitCursor;
// начало действия инструмента
isDrawingNow = true;
isFileEdited |= activeToolRef->IsMutating();
// обновляем предпросмотр картинки в случае если его будем показывать сейчас
// см. функцию события OnPaint
if (activeToolRef->IsMutating() &&!activeToolRef->IsMovementPermanent()) {
real_image_tmp = gcnew Bitmap(real_image);
}
activeToolRef->BeginDraw(real_image, e->Location);
needInvalidation = true;
pictureBox1->Invalidate();
this->Cursor = prevCursor;
}
private: System::Void pictureBox1_MouseMove(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) {
// только на левую кнопку
if (!isDrawingNow) return;
// начало действия инструмента
if (!activeToolRef->IsMovementPermanent()) {
real_image_tmp = gcnew Bitmap(real_image);
g_tmp = Graphics::FromImage(real_image_tmp);
}
else {
g_tmp = g;
}
activeToolRef->ContinueDraw(g_tmp, e->Location);
needInvalidation = true;
pictureBox1->Invalidate();
|
}
private: System::Void pictureBox1_MouseUp(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) {
// только на левую кнопку
if (!isDrawingNow) return;
// начало действия инструмента
activeToolRef->EndDraw(g, e->Location);
needInvalidation = true;
pictureBox1->Invalidate();
isDrawingNow = false;
}
private: System::Void pictureBox1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e) {
if (!needInvalidation) return;
// иначе показываем предпросмотр
if(activeToolRef->IsMutating() &&!activeToolRef->IsMovementPermanent()) {
pictureBox1->Image = real_image_tmp;
}
// если движения отражаются в конечном рисунке, показываем реальную картинку
else {
pictureBox1->Image = real_image;
}
needInvalidation = false;
}