В данном уроке мы разберём:
1. Как создавать и обрабатывать внути-игровые окна
2. Как изменить картинку курсора
Видеоверсия: no
Итак, нам потребуются создать следующие классы:
Design – дизайн
DesignWindow – дизайн окна
Window – окно
MWindows – система окон (окна)
DesignCursor – дизайн курсора
Cursor – курсор
Mouse – мышь
Каждый класс находится в отдельных файлах .h и .cpp
Я не буду объяснять элементарные вещи, которые были разобраны в предыдущих уроках.
Design – Дизайн
Дизайн – это класс для обработки определённых картинок.
Ниже представлены дизайны для окон и мышки
Для создания данного класса нам потребуется вот это
1 2 3 |
sf::String filename; // Имя файла дизайна sf::Image image; // картинка целого дизайна std::vector<sf::Image> vecImage; // Обрезанные картинки |
и функции, с помощью которых можно разрезать дизайн и получить картинки
Для загрузки дизайна будем использовать вот эту функцию.
1 2 3 4 5 6 7 8 9 |
bool Design::loadImage(sf::String filename) { vecImage.reserve(0); this->filename = filename; if(image.loadFromFile(filename)) return true; return false; } |
Далее самое интересное, как мы будем резать дизайн.
Этим у нас занимается функция designToImage()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
bool Design::designToImage(sf::Image* image1, sf::Image* image2, int numCellWidth, int numCellHeight, int sideCell) { // Если в ячейке будет картинка, то W и H её ширина и высота соответственно int W = -1; // ширина int H = -1; // высота int imin = numCellWidth * sideCell; int imax = imin + sideCell; int jmin = numCellHeight * sideCell; int jmax = jmin + sideCell; for(int j = jmin; j < jmax; j++) { for(int i = imin; i < imax; i++) { if(image1->getPixel(i, j) != sf::Color(255,255,255,0)) {// Если пиксель не пустой if( W < i-imin) W++; if( H < j-jmin) H++; } } } if(W != -1 && H != -1) { W++; H++; // Фича 31.03.2016 8:40 МСК // нужная поправка на 1 пиксель image2->create(W, H, sf::Color(255, 255, 255, 255)); image2->copy(*image1, 0, 0, sf::IntRect(imin,jmin, imin+W,jmin+H)); return true; } return false; } |
Входные параметры:
1. Sf::Image* image1 – ссылка на картинку дизайна, которую будем резать.
2. sf::Image* image2 – ссылка на картинку, в которую будем вставлять часть дизайна
3. int numCellWidth – номер ячейки по ширине
4. int numCellHeight – номер ячейки по высоте
5. int sideCell – размеры ячейки
Данная функция обрезает картинку примерно так
Внутренний алгоритм поручаю понять самостоятельно.
Немножко о методах sf::Image можно найти тут.
Перед использованием данной функции убедитесь что у вас имеются картинки image1 и image2.
Исходный код класса Design приложен к статье или можно найти вот тут.
Далее мы рассмотрим классы DesignCursor и DesignWindow.
DesignCursor – Дизайн Курсора
В этом классе мы просто укажем как нам резать дизайны курсоров.
Важным моментом является то, что сначала нужно заполнить вектор vecImage пустыми картинками, а потом уже создавать дизайн курсора.
1 2 3 4 5 6 7 8 9 |
void DesignCursor::data(void) { vecImage.reserve(6); for(int i=0; i<6; i++) { sf::Image img; vecImage.push_back(img); } } |
И собственно конструктор
1 2 3 4 5 6 7 8 9 10 11 12 13 |
DesignCursor::DesignCursor(sf::String filename) { data(); if(loadImage(filename) == true) { designToImage(&image, &vecImage[0], 0, 0, 32); // Курсор designToImage(&image, &vecImage[1], 1, 0, 32); // Перемещение designToImage(&image, &vecImage[2], 2, 0, 32); // Вертикальные designToImage(&image, &vecImage[3], 3, 0, 32); // Горизонтальные designToImage(&image, &vecImage[4], 4, 0, 32); // Лево-верх <-> Право-низ designToImage(&image, &vecImage[5], 5, 0, 32); // Лево-низ <-> Право-верх } } |
DesignWindow – Дизайн Окна
Аналог класса DesignCursor Только для окон.
1 2 3 4 5 6 7 8 |
void DesignWindow::data(void) { for(int i=0; i<9; i++) { sf::Image image; vecImage.push_back(image); } } |
А в этом классе я захотел разделить конструктор и загрузку.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
bool DesignWindow::load(sf::String filename) { if(loadImage(filename)) { data(); // Расположение картинок в векторе // 0 4 1 // 5 8 6 // 2 7 3 // Режем image и заполняем картинки vecImage[0-8] designToImage(&image, &vecImage[0], 0, 0, 32); if(!designToImage(&image, &vecImage[1], 2, 0, 32)) { designToImage(&image, &vecImage[1], 0, 0, 32); vecImage[1].flipHorizontally(); } designToImage(&image, &vecImage[2], 0, 2, 32); if(!designToImage(&image, &vecImage[3], 2, 2, 32)) { designToImage(&image, &vecImage[3], 0, 2, 32); vecImage[3].flipHorizontally(); } designToImage(&image, &vecImage[4], 1, 0, 32); designToImage(&image, &vecImage[5], 0, 1, 32); if(!designToImage(&image, &vecImage[6], 2, 1, 32)) { designToImage(&image, &vecImage[6], 0, 1, 32); vecImage[6].flipHorizontally(); } designToImage(&image, &vecImage[7], 1, 2, 32); designToImage(&image, &vecImage[8], 1, 1, 32); return true; } return false; } |
Тут есть небольшая хитрость.
Если в правом столбце нет картинок, то они берутся из левого, и разворачиваются по горизонтали.
Так-же если дизайна с таким filename не существует, то функция не будет его обрабатывать, а возвратит false, что очень важно.
Почему я заполняю vecImage именно в таком порядке, объясню когда будем разбирать класс Window.
Window – Окно
Очень сложный класс. Постараюсь разобрать все аспекты и очень кратко.
Нам потребуются 2 вспомогательные структуры:
1. winSave – служит для хранения позиции и размеров окна
2. winSprite – аналог sf::Sprite с доп.методами и параметрами. Хранит в себе 9 спрайтов для рисования окна.
Наше окно имеет координаты нахождения и размеры текущие, и предельные.
1 2 3 4 5 6 7 8 9 10 |
int x; int y; int width_current; int width_min; int width_max; int height_current; int height_min; int height_max; |
Так-же наше окно имеет флажки
1 2 3 |
bool isMove; // Можно перемещать окно? bool isConsiderBorder; // Учитывать границы? bool isChangeSize; // Можно изменять размеры? |
С помощью них можно сделать такие фишки как:
– Не выходить за края окна Программы.
– Нельзя изменять размеры (Пример: панель инвентаря)
– Можно изменять размеры (Приме: окно переписки)
– Запретить передвигать окно (Пример: мини-карта)
– и т.д.
Ещё нам потребуется номер текущего дизайна, это если приспичит менять дизайн окна в процессе программы (например в настройках); и непонятный параметр высоты.
1 2 |
int numDesCur; // номер дизайна текущего в vecDesign int height_x; |
А так-же 3 вектора
1 2 3 |
std::vector<DesignWindow*> vecDesign; // Вектор для дизайнов std::vector<winSave*> vecSave; // std::vector<winSprite*> vecWinSprite; // Вектор из 9 спрайтов |
Всё вместе это будет выглядеть как
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
protected: std::vector<DesignWindow*> vecDesign; // Вектор для 1 дизайна std::vector<winSave*> vecSave; // std::vector<winSprite*> vecWinSprite; // Вектор из 9 спрайтов int numDesCur; // номер дизайна текущего в vecDesign int x; int y; int width_current; int width_min; int width_max; int height_current; int height_min; int height_max; int height_x; bool isMove; // Можно перемещать окно? bool isConsiderBorder; // Учитывать границы? bool isChangeSize; // Можно изменять размеры? |
Сначала рассмотрим структуры.
winSprite
В vecWinSprite расположено 9 экземпляров winSprite.
каждый winSptite содержит sf::Sprite, который содержит один из этих кусочков окна
winSprite расположены в vecWinSprite в следующем порядке
Помните я вам обещал рассказать почему загрузка происходит именно таким образом? Так вот, если начать рисовать окно с конца вектора, то можно избежать некоторых сбоев в прорисовке окна.
w, h – начальная ширина и высота (совпадают с w и h image0)
wc и wh – текущие ширина и высота
dx и dy – смещение относительно позиции окна
image0 – изначальная картинка
image – текущая картинка
Все методы довольно простые кроме одного, это
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
void setScale(int factorX, int factorY) { bool bx = factorX > 1; bool by = factorY > 1; if((bx && !by) ||(!bx && by)) { // Если увеличение только в 1 сторону sprite.setScale(factorX, factorY); wc = w*factorX; hc = h*factorY; } else if(bx && by) { // Если изменение размеров в обе стороны sf::Image img; img.create(w, factorY, sf::Color(255,0,0,255)); img.copy(image, 0, 0, sf::IntRect(0,0,wc,hc)); // копируем в неё уже имеющуюся image if(factorY > hc) { for(int i = hc; i<factorY; i+=h) img.copy(image0, 0, i, sf::IntRect(0,0,w,h)); } setImage1(img); setScale(factorX, 1); } } |
Попробуем разобраться
…………….
Мы должны как-то создавать окно и задавать его размеры и свойства.
Для этого будем использовать конструкторы. В будущем надо вынести в отдельные функции.
1 2 3 4 5 6 7 |
Window::Window(int width, int height) { data(); width_current = width; height_current = height; } |
или
1 2 3 4 5 6 7 8 9 10 |
Window::Window(int width, int height, bool isMove, bool isConsiderBorder, bool isChangeSize) { data(); width_current = width; height_current = height; this->isMove = isMove; this->isConsiderBorder = isConsiderBorder; this->isChangeSize = isChangeSize; } |
Функция data() сами знаете для чего используется
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
void Window::data(void) { numDesCur = -1; // пока дизайна нет' x = 0; y = 0; width_min = 50; height_min = 50; width_max = 0; height_max = 0; height_x = 0; isMove = true; isConsiderBorder = false; isChangeSize = false; // === vector === vecDesign.reserve(1); vecWinSprite.reserve(9); for(int i=0; i<9; i++) vecWinSprite.push_back(new winSprite()); vecSave.reserve(1); winSave* win = new winSave(); vecSave.push_back(win); } |
Я пока не делал ориентацию на максимальный размер окна, так что по 0 .
..
Функция main
Перед нами файл main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
#include "stdafx.h" #include "Window.h" #include "MWindows.h" #include "mouse.h" #include "keyboard.h" using namespace std; int main() { MWindows* w0 = new MWindows(); //sf::String filename = "WinDos.png"; //sf::String filename = "Windows8.1/Windows8.1.png"; sf::String filename = "flyff/flyff.png"; sf::RenderWindow window(sf::VideoMode(1200, 700), "kychka-pc.ru/legozaur"); Mouse* mouse = new Mouse(); mouse->setWindow(&window); mouse->setDesign("aero/aero.png"); mouse->setCursor(0); mouse->setMouseCursorVisible(false); //mouse->setCursor(0); Keyboard* kb = new Keyboard(); float time = 0; sf::Clock clock; while (window.isOpen()) { time = clock.getElapsedTime().asSeconds(); clock.restart(); sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } if(kb->getEventButton(sf::Keyboard::Num1) == 1) { Window* w = new Window(150, 150, true, false, true); w->move( 0,300); w->setDesign(filename); w0->add(w);} if(kb->getEventButton(sf::Keyboard::Num2) == 1) { Window* w = new Window(150, 150, true, true, true); w->move(150,300); w->setDesign(filename); w0->add(w);} mouse->behavior(); //window.clear(sf::Color(255, 255, 255)); window.clear(sf::Color(0, 0, 0)); w0->behavior(mouse); w0->draw(&window); mouse->draw(); window.display(); } delete(w0); delete(mouse); delete(kb); return 0; } |
Нам потребуются дополнительные классы. Допустим они у нас уже есть, мы их подключили к проекту и используем.
1 2 3 4 5 6 7 |
#include "stdafx.h" #include "Window.h" #include "MWindows.h" #include "mouse.h" #include "keyboard.h" |
Чтобы отображать много окон в 1 приложении нам нужен некий класс, который будет содержат в себе все окна и обрабатывать их, и им будет MWindows. Допустим он у нас уже имеется.
Наши действия:
1. Создаём экземпляр класса MWindows
2. Выбираем файл дизайна, у меня это будет “flyff/flyff.png”.
(Можете сделать свой дизайн в Paint.net)
1 2 3 4 |
MWindows* w0 = new MWindows(); //sf::String filename = "WinDos.png"; //sf::String filename = "Windows8.1/Windows8.1.png"; sf::String filename = "flyff/flyff.png"; |
Далее нам потребуется класс мыши. Допустим он у нас уже имеется.
Наши действия:
1. Создаём экземпляр класса мышь
2. Привязываем мышь к окну Программы (обязательно)
3. Устанавливаем дизайн курсора “aero/aero.png”
4. Устанавливаем картинку курсора – стрелочку
5. Скрываем стандартный курсор
1 2 3 4 5 |
Mouse* mouse = new Mouse(); mouse->setWindow(&window); mouse->setDesign("aero/aero.png"); mouse->setCursor(0); mouse->setMouseCursorVisible(false); |
Далее создаём экземпляр класса клавиатуры (его настраивать не надо)
1 |
Keyboard* kb = new Keyboard(); |
Далее нам надо как-то создавать окна.
Наложим на клавиши 1 или 2 обработчики событий клавиатуры.
Нам потребуется класс окна, допустим он у нас уже имеется.
Наши действия, если было нажатие на кнопку:
1. Создаём экземпляр класса окна и задаём ему параметры
150 – ширина окна
150 – высота окна
true – можно ли перемещать окно (не работает)
false – можно ли окну выходить за пределы окна Программы (работает только влево и вверх)
true – можно ли изменять размеры окна
2. перемещаем окно на 300 по Y
3. Устанавливаем дизайн окна который был выбран в начале
4. Добавляем окно в систему окон MWindows
1 2 3 4 5 6 7 |
if(getEventButton(sf::Keyboard::Num1) == 1) { Window* w = new Window(150, 150, true, false, true); move(0,300); setDesign(filename); add(w); } |
Далее у нас идёт поведение мыши, которое обновляет координаты мыши
1 |
mouse->behavior(); |
Далее,
Наша система окон имеет поведение ориентируемое на мышь, так что обработаем его
1 |
w0->behavior(mouse); |
Далее мы рисуем систему окон и курсор мыши на экране
1 2 |
w0->draw(&window); mouse->draw(); |
И повторяем всё заново.
В конце программы не забываем удалить созданное нами (аля Тарас Бульба)
1 2 3 |
delete(w0); delete(mouse); delete(kb); |
Теперь мы рассмотрим, что же у нас таит в себе Система окон MWindows
II. MWindows
MWindows.h и MWindow.cpp в конце статьи или вот тут.
Начнём рассмотрение с тех функций, которые мы вызывали в пункте I.
1 2 3 4 5 6 7 8 9 |
//... MWindows* w0 = new MWindows(); //... w0->add(w); //... w0->behavior(mouse); //... w0->draw(&window); //... |
1. MWindows(void)
1 2 3 4 5 |
// Конструктор MWindows::MWindows(void) { data(); } |
Вызывается функция data() задающая начальную информацию.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// Задаёт начальную информацию void MWindows::data(void) { flag1 = false; // Нажатия на окно не было flag2 = false; // Нажатия в зоне перетаскивания не было flag3 = false; // Нажатия в зоне растягивания не было j = 0; // номер направления изменения размера vecWindow.reserve(1); // 4 чиста в vecInt - 0,1 - позиция окна, 2,3 позиция мыши в момент нажатия vecInt.reserve(4); for(int i=0; i<4; i++) vecInt.push_back(0); } |
2. void add(Window* window)
Добавляет окно в vecWindow
1 2 3 4 5 6 |
// Добавляет окно в систему окон void MWindows::add(Window* window) { window->setVector(&vecInt); // передаём ссылку на вектор координат в окно vecWindow.push_back(window); // добавляем окно в вектор окон } |
Каждое окно содержит ссылку на вектор vecInt класса MWindows, т.е. ссылку на вектор, в котором хранятся координаты окна и мыши в момент нажатия ЛКМ.
3. void behavior(Mouse* mouse)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
// Обработчик событий (поведение окон) void MWindows::behavior(Mouse* mouse) { // Записываем координаты мыши int x = mouse->getX(); int y = mouse->getY(); int iNum = -1; // Обработчик ЛКМ switch(mouse->getEventButton(sf::Mouse::Left)) { case 0: for(std::vector<Window*>::iterator it = vecWindow.begin(); it != vecWindow.end(); it++) { if((*it)->check1(&x, &y)) { iNum = it - vecWindow.begin(); // В iNum зписываеться номер окна которое выше break; } } // Если окно есть if(iNum != -1) { // записываем в zzz направление растяжения numRote = vecWindow[iNum]->check3(&x, &y); // если растяжение есть if(numRote != -1) { // меняем курсор if(numRote == 0 || numRote == 3) mouse->setCursor(4); if(numRote == 1 || numRote == 2) mouse->setCursor(5); if(numRote == 4 || numRote == 7) mouse->setCursor(2); if(numRote == 5 || numRote == 6) mouse->setCursor(3); } // иначе если перемещение else if(vecWindow[iNum]->check2(&x, &y)) { // меняем курсор mouse->setCursor(1); } else { // иначе меняем курсор на стандарт mouse->setCursor(0); } } else { // иначе меняем курсор на стандарт mouse->setCursor(0); } break; // ЛКМ: нажатие (1 раз) case 1: // Проходим по всем окнам, начиная с первого for(std::vector<Window*>::iterator it = vecWindow.begin(); it != vecWindow.end(); it++) { // Проверка: было ли нажатие мыши в окне if((*it)->check1(&x, &y) == true) { // Если нажатие было (но хз зачем) flag1 = true; // Запоминаем координаты окна и мыши vecInt[0] = (*it)->getPositionX(); vecInt[1] = (*it)->getPositionY(); vecInt[2] = x; vecInt[3] = y; // Сортируем окна вынося на первый план то, которое было выделено sort1(it - vecWindow.begin()); vecWindow[0]->save(); // Если клик мыши был по растягиваемой зоне numRote = vecWindow[0]->check3(&x, &y); if(numRote != -1) flag3 = true; // Если клик мыши был по перетаскиваемой зоне if(vecWindow[0]->check2(&x, &y)) flag2 = true; break; // предварительно выходим из вектора окно, т.к. мы нашли окно на которое кликнули. } } // Точка, где будет программа после нахождения или не нахождения окна break; // ЛКМ: нажата case 2: if(flag3) vecWindow[0]->changeSize(mouse, numRote); else if(flag2) vecWindow[0]->changePosition(mouse); break; // ЛКМ: отжатие (1 раз) case 3: flag1 = false; flag2 = false; flag3 = false; numRote = 0; break; } } |
Разбор полётов:
Общий вид функции
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// Записываем координаты мыши int x = mouse->getX(); int y = mouse->getY(); int iNum = -1; // Обработчик ЛКМ switch(mouse->getEventButton(sf::Mouse::Left)) { case 0: // Если ЛКМ не нажата //... break; case 1: // Если ЛКМ нажимается (1раз) //... break; case 2: // Если ЛКМ до сих пор нажата //... break; case 3: // Если ЛКМ отжимается (1раз) //... break; } |
Разберём всё по порядку
case 0
Что происходит в программе когда мышь не нажата?
Когда мышь не нажата, может изменятьcя картинка курсора, если он зайдёт в зону растяжения и собственно всё.
Итак, проходим по всем окнам
1 2 3 4 5 |
// Проходим по всем окнам for(std::vector<Window*>::iterator it = vecWindow.begin(); it != vecWindow.end(); it++) { // } |
Нам бы надо найти окно, на которое зашла мышка.
Специально для этого у каждого окна существует функция check1(&x, &y) которая принимает ссылки на координаты мыши и возвращает true, если эти координаты принадлежат данному окну.
1 2 3 4 5 6 7 8 9 |
// Проходим по всем окнам for(std::vector<Window*>::iterator it = vecWindow.begin(); it != vecWindow.end(); it++) { // Если мышка зашла на окно if((*it)->check1(&x, &y)) { // } } |
Если мы нашли окно, на которое зашла мышка, то нам надо запомнить номер этого окна и выйти из цикла. (номер запишем в iNum)
Если не написать break, то данная конструкция возьмёт самое нижнее окно, на которое наведён курсор, что не есть гуд.
1 2 3 4 5 6 7 8 9 10 |
// Проходим по всем окнам for(std::vector<Window*>::iterator it = vecWindow.begin(); it != vecWindow.end(); it++) { // Если мышка зашла на окно if((*it)->check1(&x, &y)) { iNum = it - vecWindow.begin(); break; } } |
Далее:
А что если мышка сейчас не на окне? Тогда картинку изменять не надо.
1 2 3 4 5 |
// Проверяем, что мышка точно в окне if(iNum != -1) { // } |
Если мышка входит в окно, то мы должны проверить, а вдруг мышка находиться на зонах растягивания. Пример на картинке. В третьем случае мышка зашла в зону растягивания, и соответственно, должна поменять картинку курсора
Для проверки входимости в зоны растяжения у каждого окна существует функция check3(&x, &y), которая возвращает 0-7 если мышка находится в зонах растягивания и -1 если нет.
Почему именно так? позже объясню.
1 2 3 4 5 6 7 8 9 |
// Проверяем, что мышка точно на окне if(iNum != -1) { numRote = vecWindow[iNum]->check3(&x, &y); if(numRote != -1) { // Изменяем курсор } } |
Добавляем установку курсора в соответствии с полученным значением zzz
1 2 3 4 5 6 7 8 9 10 11 12 |
// Проверяем, что мышка точно на окне if(iNum != -1) { numRote = vecWindow[iNum]->check3(&x, &y); if(numRote != -1) { if(numRote == 0 || numRote == 3) mouse->setCursor(4); if(numRote == 1 || numRote == 2) mouse->setCursor(5); if(numRote == 4 || numRote == 7) mouse->setCursor(2); if(numRote == 5 || numRote == 6) mouse->setCursor(3); } } |
В остальных случаях либо зона перемещения, либо никакой.
Я решил что в зонах перемещения курсор тоже будет менять картинку.
Обычный курсор тоже нужно устанавливать!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
if(iNum != -1) { numRote = vecWindow[iNum]->check3(&x, &y); if(numRote != -1) { if(numRote == 0 || numRote == 3) mouse->setCursor(4); if(numRote == 1 || numRote == 2) mouse->setCursor(5); if(numRote == 4 || numRote == 7) mouse->setCursor(2); if(numRote == 5 || numRote == 6) mouse->setCursor(3); } else if(vecWindow[iNum]->check2(&x, &y)) { mouse->setCursor(1); } else { mouse->setCursor(0); } } else { mouse->setCursor(0); } |
В итоге это будет выглядеть вот так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
case 0: if(iNum != -1) { numRote = vecWindow[iNum]->check3(&x, &y); if(numRote != -1) { if(numRote == 0 || numRote == 3) mouse->setCursor(4); if(numRote == 1 || numRote == 2) mouse->setCursor(5); if(numRote == 4 || numRote == 7) mouse->setCursor(2); if(numRote == 5 || numRote == 6) mouse->setCursor(3); } else if(vecWindow[iNum]->check2(&x, &y)) { mouse->setCursor(1); } else { mouse->setCursor(0); } } else { mouse->setCursor(0); } break; |
case 1
Что происходит в программе когда нажимается ЛКМ.
В нашем случае возможны 3 варианта:
– ничего
– начало изменений координат окна
– начало изменения размеров окна
Итак, давайте выясним это!
Перебираем окна
1 2 3 4 5 |
// Проходим по всем окнам, начиная с первого for(std::vector<Window*>::iterator it = vecWindow.begin(); it != vecWindow.end(); it++) { // } |
Проверим, было ли нажатие на 1 из окон
1 2 3 4 5 6 7 8 9 |
// Проходим по всем окнам, начиная с первого for(std::vector<Window*>::iterator it = vecWindow.begin(); it != vecWindow.end(); it++) { // Проверка: было ли нажатие мыши в окне if((*it)->check1(&x, &y) == true) { // } } |
Если было нажатие, то неплохо было бы запомнить координаты окна и координаты мыши в момент нажатия, на будущее. Вставлять будем в vecInt следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// Проходим по всем окнам, начиная с первого for(std::vector<Window*>::iterator it = vecWindow.begin(); it != vecWindow.end(); it++) { // Проверка: было ли нажатие мыши в окне if((*it)->check1(&x, &y) == true) { // Запоминаем координаты окна и мыши vecInt[0] = (*it)->getPositionX(); vecInt[1] = (*it)->getPositionY(); vecInt[2] = x; vecInt[3] = y; } } |
Далее нам необходимо чтобы окно, по которому мы кликнули поместилось на передний план.
Выглядеть это будет следующим образом
Функция sort1(int) этим и занимается
1 2 3 4 5 6 7 8 9 10 |
// Сортировка окон void MWindows::sort1(int num) { Window* window = vecWindow[num]; for(int i=num; i>0; i--) { vecWindow[i] = vecWindow[i-1]; } vecWindow[0] = window; } |
Это выглядит примерно так
После сортировки выделенное окно становиться в vecImage[0]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// Проходим по всем окнам, начиная с первого for(std::vector<Window*>::iterator it = vecWindow.begin(); it != vecWindow.end(); it++) { // Проверка: было ли нажатие мыши в окне if((*it)->check1(&x, &y) == true) { // Запоминаем координаты окна и мыши vecInt[0] = (*it)->getPositionX(); vecInt[1] = (*it)->getPositionY(); vecInt[2] = x; vecInt[3] = y; sort1(it - vecWindow.begin()); } } |
Теперь на всякий случай сохраним размеры окна с помощью функции save();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Проходим по всем окнам, начиная с первого for(std::vector<Window*>::iterator it = vecWindow.begin(); it != vecWindow.end(); it++) { // Проверка: было ли нажатие мыши в окне if((*it)->check1(&x, &y) == true) { // Запоминаем координаты окна и мыши vecInt[0] = (*it)->getPositionX(); vecInt[1] = (*it)->getPositionY(); vecInt[2] = x; vecInt[3] = y; sort1(it - vecWindow.begin()); vecWindow[0]->save(); } } |
Дальше нам нужно проверить, был ли клик по зоне растяжения или по зоне перетаскивания.
Для этого добавим следующие строчки
1 2 3 4 5 6 7 8 |
// Если клик мыши был по растягиваемой зоне numRoute = vecWindow[0]->check3(&x, &y); if(numRoute != -1) flag3 = true; // Если клик мыши был по перетаскиваемой зоне if(vecWindow[0]->check2(&x, &y)) flag2 = true; |
думаю, тут и так всё понятно. Обрабатываем оба случая входа в зону и помечаем флажком если положительный вход в зону.
Так-же после всего этого нам нужно выйти из цикла поиска окон, иначе функция продолжит перебирать окна, что не есть правильно.
В итоге у нас будет что-то вроде этого
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
case 1: for(std::vector<Window*>::iterator it = vecWindow.begin(); it != vecWindow.end(); it++) { if((*it)->check1(&x, &y) == true) { flag1 = true; vecInt[0] = (*it)->getPositionX(); vecInt[1] = (*it)->getPositionY(); vecInt[2] = x; vecInt[3] = y; sort1(j); vecWindow[0]->save(); numRoute = vecWindow[0]->check3(&x, &y); if(numRoute != -1) flag3 = true; if(vecWindow[0]->check2(&x, &y)) flag2 = true; break; } } break; |
case 2
Что происходит в программе когда зажата ЛКМ.
В нашем случае возможны 3 ситуации:
– ничего
– было нажатие в зону перетаскивания, надо перемещать окно
– было нажатие в зоне растяжения, надо растягивать окно
Для этого мы в case 1 использовали flag2 и flag3.
Сначала мы проверим, будет растяжение, и если его не будет, то проверим, будет ли перемещение.
1 2 3 4 5 6 7 8 |
if(flag3 == true) { // надо растягивать } else if(flag2 == true) { // надо перемещать } |
Допустим у каждого окна уже есть функция растягивания и перемещения,
В нашем случае это
1 |
vecWindow[0]->changeSize(mouse, numRote); |
и
1 |
vecWindow[0]->changePosition(mouse); |
Они за нас всё сделает, Отлично! ООП великолепно!
В итоге мы получим
1 2 3 4 5 6 |
// ЛКМ: нажата case 2: if(flag3) vecWindow[0]->changeSize(mouse, numRote); else if(flag2) vecWindow[0]->changePosition(mouse); break; |
Компактно, не правда ли?
case 3
Что происходит когда мышка отпускается?
Окна прекращает перетаскиваться или растягиваться и собственно всё.
В нашем случае мы просто обнуляем флаги.
1 2 3 4 5 6 7 |
// ЛКМ: отжатие (1 раз) case 3: flag1 = false; flag2 = false; flag3 = false; numRote = 0; break; |
Собственно, это пока всё с поведением системы окон.
void draw(sf::RenderWindow* window)
Рисует во входящем окне все окна с конца в начало вектора.
Таким образом организоывваеться правильное отображение окон.
1 2 3 4 5 6 7 |
// Отрисовка во входящем окне void MWindows::draw(sf::RenderWindow* window) { int imax = vecWindow.size()-1; for(int i=imax; i>=0; i--) vecWindow[i]->draw(window); } |
Архив будет приложен в будущем.
..