diff options
| author | Simon Robertshaw <simon@hardwired.org.uk> | 2013-03-24 12:24:17 (GMT) |
|---|---|---|
| committer | Simon Robertshaw <simon@hardwired.org.uk> | 2013-03-24 12:24:17 (GMT) |
| commit | 9b5b85f9b01cbda7ef9a7ec2a15b2a35630a5b9d (patch) | |
| tree | ac7d040253b459ce102e476cb19ab59e3cfa90d7 /src/gui | |
| parent | 6bf98ccdca39936a3c51367862eed7c49f8786ec (diff) | |
| parent | bdc69f31c0be94191015838886bdcc2bc67f1acb (diff) | |
| download | powder-9b5b85f9b01cbda7ef9a7ec2a15b2a35630a5b9d.zip powder-9b5b85f9b01cbda7ef9a7ec2a15b2a35630a5b9d.tar.gz | |
Merge branch 'reorganisation' of github.com:FacialTurd/The-Powder-Toy
Diffstat (limited to 'src/gui')
147 files changed, 22019 insertions, 0 deletions
diff --git a/src/gui/Style.cpp b/src/gui/Style.cpp new file mode 100644 index 0000000..29e1d40 --- /dev/null +++ b/src/gui/Style.cpp @@ -0,0 +1,18 @@ +#include <iostream> + +#include "gui/Style.h" +#include "gui/interface/Colour.h" + +namespace style { + ui::Colour Colour::InformationTitle = ui::Colour(140, 140, 255); + ui::Colour Colour::WarningTitle = ui::Colour(255, 216, 32); + ui::Colour Colour::ErrorTitle = ui::Colour(255, 64, 32); + + ui::Colour Colour::ConfirmButton = ui::Colour(255, 255, 50); + + ui::Colour Colour::ActiveBorder = ui::Colour(255, 255, 255); + ui::Colour Colour::InactiveBorder = ui::Colour(100, 100, 100); + + ui::Colour Colour::ActiveBackground = ui::Colour(50, 50, 50); + ui::Colour Colour::InactiveBackground = ui::Colour(0, 0, 0); +} diff --git a/src/gui/Style.h b/src/gui/Style.h new file mode 100644 index 0000000..7ac0a01 --- /dev/null +++ b/src/gui/Style.h @@ -0,0 +1,28 @@ +#ifndef STYLE_H_ +#define STYLE_H_ + +namespace ui { class Colour; } + +namespace style +{ + class Colour + { + public: + static ui::Colour InformationTitle; + static ui::Colour WarningTitle; + static ui::Colour ErrorTitle; + + static ui::Colour ConfirmButton; + + static ui::Colour ActiveBorder; + static ui::Colour InactiveBorder; + + static ui::Colour ActiveBackground; + static ui::Colour InactiveBackground; + }; + class Metrics + { + }; +} + +#endif diff --git a/src/gui/colourpicker/ColourPickerActivity.cpp b/src/gui/colourpicker/ColourPickerActivity.cpp new file mode 100644 index 0000000..a7ac239 --- /dev/null +++ b/src/gui/colourpicker/ColourPickerActivity.cpp @@ -0,0 +1,309 @@ +#include <algorithm> +#include <iomanip> +#include "ColourPickerActivity.h" +#include "gui/interface/Textbox.h" +#include "gui/interface/Label.h" +#include "gui/interface/Keys.h" +#include "gui/game/Tool.h" +#include "gui/Style.h" +#include "Format.h" +#include "gui/game/GameModel.h" + +ColourPickerActivity::ColourPickerActivity(ui::Colour initialColour, ColourPickedCallback * callback) : + WindowActivity(ui::Point(-1, -1), ui::Point(266, 175)), + currentHue(0), + currentSaturation(0), + currentValue(0), + mouseDown(false), + valueMouseDown(false), + callback(callback) +{ + + class ColourChange : public ui::TextboxAction + { + ColourPickerActivity * a; + public: + ColourChange(ColourPickerActivity * a) : a(a) {} + + void TextChangedCallback(ui::Textbox * sender) + { + int r, g, b, alpha; + r = format::StringToNumber<int>(a->rValue->GetText()); + g = format::StringToNumber<int>(a->gValue->GetText()); + b = format::StringToNumber<int>(a->bValue->GetText()); + alpha = format::StringToNumber<int>(a->aValue->GetText()); + if (r > 255) + r = 255; + if (g > 255) + g = 255; + if (b > 255) + b = 255; + if (alpha > 255) + alpha = 255; + + RGB_to_HSV(r, g, b, &a->currentHue, &a->currentSaturation, &a->currentValue); + a->currentAlpha = alpha; + a->UpdateTextboxes(r, g, b, alpha); + } + }; + + rValue = new ui::Textbox(ui::Point(5, Size.Y-23), ui::Point(30, 17), "255"); + rValue->SetActionCallback(new ColourChange(this)); + rValue->SetLimit(3); + rValue->SetInputType(ui::Textbox::Number); + AddComponent(rValue); + + gValue = new ui::Textbox(ui::Point(40, Size.Y-23), ui::Point(30, 17), "255"); + gValue->SetActionCallback(new ColourChange(this)); + gValue->SetLimit(3); + gValue->SetInputType(ui::Textbox::Number); + AddComponent(gValue); + + bValue = new ui::Textbox(ui::Point(75, Size.Y-23), ui::Point(30, 17), "255"); + bValue->SetActionCallback(new ColourChange(this)); + bValue->SetLimit(3); + bValue->SetInputType(ui::Textbox::Number); + AddComponent(bValue); + + aValue = new ui::Textbox(ui::Point(110, Size.Y-23), ui::Point(30, 17), "255"); + aValue->SetActionCallback(new ColourChange(this)); + aValue->SetLimit(3); + aValue->SetInputType(ui::Textbox::Number); + AddComponent(aValue); + + hexValue = new::ui::Label(ui::Point(150, Size.Y-23), ui::Point(53, 17), "0xFFFFFFFF"); + AddComponent(hexValue); + + class OkayAction: public ui::ButtonAction + { + ColourPickerActivity * a; + public: + OkayAction(ColourPickerActivity * a) : a(a) { } + void ActionCallback(ui::Button * sender) + { + int Red, Green, Blue; + Red = format::StringToNumber<int>(a->rValue->GetText()); + Green = format::StringToNumber<int>(a->gValue->GetText()); + Blue = format::StringToNumber<int>(a->bValue->GetText()); + ui::Colour col(Red, Green, Blue, a->currentAlpha); + if(a->callback) + a->callback->ColourPicked(col); + a->Exit(); + } + }; + + ui::Button * doneButton = new ui::Button(ui::Point(Size.X-45, Size.Y-23), ui::Point(40, 17), "Done"); + doneButton->SetActionCallback(new OkayAction(this)); + AddComponent(doneButton); + SetOkayButton(doneButton); + + RGB_to_HSV(initialColour.Red, initialColour.Green, initialColour.Blue, ¤tHue, ¤tSaturation, ¤tValue); + currentAlpha = initialColour.Alpha; + UpdateTextboxes(initialColour.Red, initialColour.Green, initialColour.Blue, initialColour.Alpha); +} + +void ColourPickerActivity::UpdateTextboxes(int r, int g, int b, int a) +{ + rValue->SetText(format::NumberToString<int>(r)); + gValue->SetText(format::NumberToString<int>(g)); + bValue->SetText(format::NumberToString<int>(b)); + aValue->SetText(format::NumberToString<int>(a)); + std::stringstream hex; + hex << std::hex << "0x" << std::setfill('0') << std::setw(2) << std::uppercase << a << std::setw(2) << r << std::setw(2) << g << std::setw(2) << b; + hexValue->SetText(hex.str()); +} +void ColourPickerActivity::OnTryExit(ExitMethod method) +{ + Exit(); +} + +void ColourPickerActivity::OnMouseMove(int x, int y, int dx, int dy) +{ + if(mouseDown) + { + x -= Position.X+5; + y -= Position.Y+5; + + currentHue = (float(x)/float(255))*359.0f; + currentSaturation = 255-(y*2); + + if(currentSaturation > 255) + currentSaturation = 255; + if(currentSaturation < 0) + currentSaturation = 0; + if(currentHue > 359) + currentHue = 359; + if(currentHue < 0) + currentHue = 0; + } + + if(valueMouseDown) + { + x -= Position.X+5; + y -= Position.Y+5; + + currentValue = x; + + if(currentValue > 255) + currentValue = 255; + if(currentValue < 0) + currentValue = 0; + } + + if(mouseDown || valueMouseDown) + { + int cr, cg, cb; + HSV_to_RGB(currentHue, currentSaturation, currentValue, &cr, &cg, &cb); + UpdateTextboxes(cr, cg, cb, currentAlpha); + } +} + +void ColourPickerActivity::OnMouseDown(int x, int y, unsigned button) +{ + x -= Position.X+5; + y -= Position.Y+5; + if(x >= 0 && x < 256 && y >= 0 && y <= 128) + { + mouseDown = true; + currentHue = (float(x)/float(255))*359.0f; + currentSaturation = 255-(y*2); + + if(currentSaturation > 255) + currentSaturation = 255; + if(currentSaturation < 0) + currentSaturation = 0; + if(currentHue > 359) + currentHue = 359; + if(currentHue < 0) + currentHue = 0; + } + + if(x >= 0 && x < 256 && y >= 132 && y <= 142) + { + valueMouseDown = true; + currentValue = x; + + if(currentValue > 255) + currentValue = 255; + if(currentValue < 0) + currentValue = 0; + } + + if(mouseDown || valueMouseDown) + { + int cr, cg, cb; + HSV_to_RGB(currentHue, currentSaturation, currentValue, &cr, &cg, &cb); + UpdateTextboxes(cr, cg, cb, currentAlpha); + } +} + +void ColourPickerActivity::OnMouseUp(int x, int y, unsigned button) +{ + if(mouseDown || valueMouseDown) + { + int cr, cg, cb; + HSV_to_RGB(currentHue, currentSaturation, currentValue, &cr, &cg, &cb); + UpdateTextboxes(cr, cg, cb, currentAlpha); + } + + if(mouseDown) + { + mouseDown = false; + x -= Position.X+5; + y -= Position.Y+5; + + currentHue = (float(x)/float(255))*359.0f; + currentSaturation = 255-(y*2); + + if(currentSaturation > 255) + currentSaturation = 255; + if(currentSaturation < 0) + currentSaturation = 0; + if(currentHue > 359) + currentHue = 359; + if(currentHue < 0) + currentHue = 0; + } + + if(valueMouseDown) + { + valueMouseDown = false; + + x -= Position.X+5; + y -= Position.Y+5; + + currentValue = x; + + if(currentValue > 255) + currentValue = 255; + if(currentValue < 0) + currentValue = 0; + } +} + + +void ColourPickerActivity::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + //g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); + g->fillrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3, 0, 0, 0, currentAlpha); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + + g->drawrect(Position.X+4, Position.Y+4, 258, 130, 180, 180, 180, 255); + + g->drawrect(Position.X+4, Position.Y+4+4+128, 258, 12, 180, 180, 180, 255); + + + int offsetX = Position.X+5; + int offsetY = Position.Y+5; + + + //draw color square + int lastx = -1, currx = 0; + for(int saturation = 0; saturation <= 255; saturation+=2) + { + for(int hue = 0; hue <= 359; hue++) + { + currx = clamp_flt(hue, 0, 359)+offsetX; + if (currx == lastx) + continue; + lastx = currx; + int cr = 0; + int cg = 0; + int cb = 0; + HSV_to_RGB(hue, 255-saturation, currentValue, &cr, &cg, &cb); + g->blendpixel(currx, (saturation/2)+offsetY, cr, cg, cb, currentAlpha); + } + } + + //draw brightness bar + for(int value = 0; value <= 255; value++) + for(int i = 0; i < 10; i++) + { + int cr = 0; + int cg = 0; + int cb = 0; + HSV_to_RGB(currentHue, currentSaturation, value, &cr, &cg, &cb); + + g->blendpixel(value+offsetX, i+offsetY+127+5, cr, cg, cb, currentAlpha); + } + + //draw color square pointer + int currentHueX = clamp_flt(currentHue, 0, 359); + int currentSaturationY = ((255-currentSaturation)/2); + g->xor_line(offsetX+currentHueX, offsetY+currentSaturationY-5, offsetX+currentHueX, offsetY+currentSaturationY-1); + g->xor_line(offsetX+currentHueX, offsetY+currentSaturationY+1, offsetX+currentHueX, offsetY+currentSaturationY+5); + g->xor_line(offsetX+currentHueX-5, offsetY+currentSaturationY, offsetX+currentHueX-1, offsetY+currentSaturationY); + g->xor_line(offsetX+currentHueX+1, offsetY+currentSaturationY, offsetX+currentHueX+5, offsetY+currentSaturationY); + + //draw brightness bar pointer + int currentValueX = restrict_flt(currentValue, 0, 254); + g->xor_line(offsetX+currentValueX, offsetY+4+128, offsetX+currentValueX, offsetY+13+128); + g->xor_line(offsetX+currentValueX+1, offsetY+4+128, offsetX+currentValueX+1, offsetY+13+128); +} + +ColourPickerActivity::~ColourPickerActivity() { + if(callback) + delete callback; +} + diff --git a/src/gui/colourpicker/ColourPickerActivity.h b/src/gui/colourpicker/ColourPickerActivity.h new file mode 100644 index 0000000..f1e62b7 --- /dev/null +++ b/src/gui/colourpicker/ColourPickerActivity.h @@ -0,0 +1,43 @@ +#pragma once + +#include <vector> +#include <string> +#include "Activity.h" +#include "gui/interface/Window.h" +#include "gui/interface/Textbox.h" + +class ColourPickedCallback +{ +public: + ColourPickedCallback() {} + virtual ~ColourPickedCallback() {} + virtual void ColourPicked(ui::Colour colour) {} +}; + +class ColourPickerActivity: public WindowActivity { + int currentHue; + int currentSaturation; + int currentValue; + int currentAlpha; + + bool mouseDown; + bool valueMouseDown; + + ui::Textbox * rValue; + ui::Textbox * gValue; + ui::Textbox * bValue; + ui::Textbox * aValue; + ui::Label * hexValue; + + ColourPickedCallback * callback; + + void UpdateTextboxes(int r, int g, int b, int a); +public: + virtual void OnMouseMove(int x, int y, int dx, int dy); + virtual void OnMouseDown(int x, int y, unsigned button); + virtual void OnMouseUp(int x, int y, unsigned button); + virtual void OnTryExit(ExitMethod method); + ColourPickerActivity(ui::Colour initialColour, ColourPickedCallback * callback = NULL); + virtual ~ColourPickerActivity(); + virtual void OnDraw(); +}; diff --git a/src/gui/console/ConsoleCommand.h b/src/gui/console/ConsoleCommand.h new file mode 100644 index 0000000..31e41b0 --- /dev/null +++ b/src/gui/console/ConsoleCommand.h @@ -0,0 +1,23 @@ +#ifndef CONSOLECOMMAND_H_ +#define CONSOLECOMMAND_H_ + +class ConsoleCommand +{ +public: + ConsoleCommand(std::string command, int returnStatus, std::string returnValue): + Command(command), ReturnStatus(returnStatus), ReturnValue(returnValue) + { + + } + std::string Command; + int ReturnStatus; + std::string ReturnValue; + + operator std::string() const + { + return Command; + } +}; + + +#endif /* CONSOLECOMMAND_H_ */ diff --git a/src/gui/console/ConsoleController.cpp b/src/gui/console/ConsoleController.cpp new file mode 100644 index 0000000..602636f --- /dev/null +++ b/src/gui/console/ConsoleController.cpp @@ -0,0 +1,74 @@ +#include <stack> +#include "ConsoleController.h" + +ConsoleController::ConsoleController(ControllerCallback * callback, CommandInterface * commandInterface): + HasDone(false) +{ + consoleModel = new ConsoleModel(); + consoleView = new ConsoleView(); + consoleView->AttachController(this); + consoleModel->AddObserver(consoleView); + + this->callback = callback; + this->commandInterface = commandInterface; +} + +void ConsoleController::EvaluateCommand(std::string command) +{ + if (command.substr(0, 6) == "!load ") + CloseConsole(); + int returnCode = commandInterface->Command(command); + if(command.length()) + consoleModel->AddLastCommand(ConsoleCommand(command, returnCode, commandInterface->GetLastError())); + else + CloseConsole(); +} + +void ConsoleController::CloseConsole() +{ + if(ui::Engine::Ref().GetWindow() == consoleView) + ui::Engine::Ref().CloseWindow(); +} + +std::string ConsoleController::FormatCommand(std::string command) +{ + return commandInterface->FormatCommand(command); +} + +void ConsoleController::NextCommand() +{ + int cIndex = consoleModel->GetCurrentCommandIndex(); + if(cIndex < consoleModel->GetPreviousCommands().size()) + consoleModel->SetCurrentCommandIndex(cIndex+1); +} + +void ConsoleController::PreviousCommand() +{ + int cIndex = consoleModel->GetCurrentCommandIndex(); + if(cIndex > 0) + consoleModel->SetCurrentCommandIndex(cIndex-1); +} + +void ConsoleController::Exit() +{ + if(ui::Engine::Ref().GetWindow() == consoleView) + ui::Engine::Ref().CloseWindow(); + if(callback) + callback->ControllerExit(); + HasDone = true; +} + +ConsoleView * ConsoleController::GetView() +{ + return consoleView; +} + +ConsoleController::~ConsoleController() { + if(ui::Engine::Ref().GetWindow() == consoleView) + ui::Engine::Ref().CloseWindow(); + if(callback) + delete callback; + delete consoleModel; + delete consoleView; +} + diff --git a/src/gui/console/ConsoleController.h b/src/gui/console/ConsoleController.h new file mode 100644 index 0000000..cf8b36e --- /dev/null +++ b/src/gui/console/ConsoleController.h @@ -0,0 +1,31 @@ +#ifndef CONSOLECONTROLLER_H_ +#define CONSOLECONTROLLER_H_ + +#include <string> +#include "Controller.h" +#include "ConsoleView.h" +#include "ConsoleModel.h" +#include "ConsoleCommand.h" +#include "cat/CommandInterface.h" + +class ConsoleModel; +class ConsoleView; +class ConsoleController { + ControllerCallback * callback; + ConsoleView * consoleView; + ConsoleModel * consoleModel; + CommandInterface * commandInterface; +public: + bool HasDone; + ConsoleController(ControllerCallback * callback, CommandInterface * commandInterface); + std::string FormatCommand(std::string command); + void EvaluateCommand(std::string command); + void NextCommand(); + void PreviousCommand(); + void Exit(); + void CloseConsole(); + ConsoleView * GetView(); + virtual ~ConsoleController(); +}; + +#endif /* CONSOLECONTROLLER_H_ */ diff --git a/src/gui/console/ConsoleModel.cpp b/src/gui/console/ConsoleModel.cpp new file mode 100644 index 0000000..ceb6b32 --- /dev/null +++ b/src/gui/console/ConsoleModel.cpp @@ -0,0 +1,75 @@ +#include "client/Client.h" +#include "ConsoleModel.h" + +ConsoleModel::ConsoleModel() { + std::vector<std::string> previousHistory = Client::Ref().GetPrefStringArray("Console.History"); + for(std::vector<std::string>::reverse_iterator iter = previousHistory.rbegin(), end = previousHistory.rend(); iter != end; ++iter) + { + if(previousCommands.size()<25) + { + previousCommands.push_front(ConsoleCommand(*iter, 0, "")); + currentCommandIndex = previousCommands.size(); + } + } +} + +void ConsoleModel::AddObserver(ConsoleView * observer) +{ + observers.push_back(observer); + observer->NotifyPreviousCommandsChanged(this); +} + +int ConsoleModel::GetCurrentCommandIndex() +{ + return currentCommandIndex; +} + +void ConsoleModel::SetCurrentCommandIndex(int index) +{ + currentCommandIndex = index; + notifyCurrentCommandChanged(); +} + +ConsoleCommand ConsoleModel::GetCurrentCommand() +{ + if(currentCommandIndex < 0 || currentCommandIndex >= previousCommands.size()) + { + return ConsoleCommand("", 0, ""); + } + return previousCommands[currentCommandIndex]; +} + +void ConsoleModel::AddLastCommand(ConsoleCommand command) +{ + previousCommands.push_back(command); + if(previousCommands.size()>25) + previousCommands.pop_front(); + currentCommandIndex = previousCommands.size(); + notifyPreviousCommandsChanged(); +} + +std::deque<ConsoleCommand> ConsoleModel::GetPreviousCommands() +{ + return previousCommands; +} + +void ConsoleModel::notifyPreviousCommandsChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyPreviousCommandsChanged(this); + } +} + +void ConsoleModel::notifyCurrentCommandChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyCurrentCommandChanged(this); + } +} + +ConsoleModel::~ConsoleModel() { + Client::Ref().SetPref("Console.History", std::vector<std::string>(previousCommands.begin(), previousCommands.end())); +} + diff --git a/src/gui/console/ConsoleModel.h b/src/gui/console/ConsoleModel.h new file mode 100644 index 0000000..312739a --- /dev/null +++ b/src/gui/console/ConsoleModel.h @@ -0,0 +1,28 @@ +#ifndef CONSOLEMODEL_H_ +#define CONSOLEMODEL_H_ + +#include <vector> +#include <deque> +#include "ConsoleView.h" +#include "ConsoleCommand.h" + +class ConsoleView; +class ConsoleModel { + int currentCommandIndex; + std::vector<ConsoleView*> observers; + std::deque<ConsoleCommand> previousCommands; + void notifyPreviousCommandsChanged(); + void notifyCurrentCommandChanged(); +public: + int GetCurrentCommandIndex(); + void SetCurrentCommandIndex(int index); + ConsoleCommand GetCurrentCommand(); + + std::deque<ConsoleCommand> GetPreviousCommands(); + ConsoleModel(); + void AddObserver(ConsoleView * observer); + void AddLastCommand(ConsoleCommand command); + virtual ~ConsoleModel(); +}; + +#endif /* CONSOLEMODEL_H_ */ diff --git a/src/gui/console/ConsoleView.cpp b/src/gui/console/ConsoleView.cpp new file mode 100644 index 0000000..cf398ae --- /dev/null +++ b/src/gui/console/ConsoleView.cpp @@ -0,0 +1,102 @@ +#include "ConsoleView.h" +#include "gui/interface/Keys.h" + +ConsoleView::ConsoleView(): + ui::Window(ui::Point(0, 0), ui::Point(XRES+BARSIZE, 150)), + commandField(NULL) +{ + class CommandHighlighter: public ui::TextboxAction + { + ConsoleView * v; + public: + CommandHighlighter(ConsoleView * v_) { v = v_; } + virtual void TextChangedCallback(ui::Textbox * sender) + { + sender->SetDisplayText(v->c->FormatCommand(sender->GetText())); + } + }; + commandField = new ui::Textbox(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), ""); + commandField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + commandField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + commandField->SetActionCallback(new CommandHighlighter(this)); + AddComponent(commandField); + FocusComponent(commandField); + commandField->SetBorder(false); +} + +void ConsoleView::DoKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + switch(key) + { + case KEY_ESCAPE: + case '`': + if (character != '~') + c->CloseConsole(); + else + Window::DoKeyPress(key, character, shift, ctrl, alt); + break; + case KEY_RETURN: + case KEY_ENTER: + c->EvaluateCommand(commandField->GetText()); + commandField->SetText(""); + commandField->SetDisplayText(""); + break; + case KEY_DOWN: + c->NextCommand(); + break; + case KEY_UP: + c->PreviousCommand(); + break; + default: + Window::DoKeyPress(key, character, shift, ctrl, alt); + break; + } +} + +void ConsoleView::NotifyPreviousCommandsChanged(ConsoleModel * sender) +{ + for(int i = 0; i < commandList.size(); i++) + { + RemoveComponent(commandList[i]); + delete commandList[i]; + } + commandList.clear(); + std::deque<ConsoleCommand> commands = sender->GetPreviousCommands(); + int currentY = Size.Y - 32; + if(commands.size()) + for(int i = commands.size()-1; i >= 0; i--) + { + if(currentY <= 0) + break; + ui::Label * tempLabel = new ui::Label(ui::Point(Size.X/2, currentY), ui::Point(Size.X/2, 16), commands[i].ReturnValue); + tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + commandList.push_back(tempLabel); + AddComponent(tempLabel); + tempLabel = new ui::Label(ui::Point(0, currentY), ui::Point(Size.X/2, 16), commands[i].Command); + tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + commandList.push_back(tempLabel); + AddComponent(tempLabel); + currentY-=16; + } +} + +void ConsoleView::NotifyCurrentCommandChanged(ConsoleModel * sender) +{ + commandField->SetText(sender->GetCurrentCommand().Command); + commandField->SetDisplayText(c->FormatCommand(commandField->GetText())); +} + + +void ConsoleView::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + g->fillrect(Position.X, Position.Y, Size.X, Size.Y, 0, 0, 0, 110); + g->draw_line(Position.X, Position.Y+Size.Y-16, Position.X+Size.X, Position.Y+Size.Y-16, 255, 255, 255, 160); + g->draw_line(Position.X, Position.Y+Size.Y, Position.X+Size.X, Position.Y+Size.Y, 255, 255, 255, 200); +} + +ConsoleView::~ConsoleView() { +} + diff --git a/src/gui/console/ConsoleView.h b/src/gui/console/ConsoleView.h new file mode 100644 index 0000000..22f172c --- /dev/null +++ b/src/gui/console/ConsoleView.h @@ -0,0 +1,30 @@ +#ifndef CONSOLEVIEW_H_ +#define CONSOLEVIEW_H_ + +#include <vector> +#include <queue> +#include "gui/interface/Label.h" +#include "gui/interface/Window.h" +#include "ConsoleController.h" +#include "ConsoleModel.h" +#include "gui/interface/Textbox.h" +#include "ConsoleCommand.h" + + +class ConsoleController; +class ConsoleModel; +class ConsoleView: public ui::Window { + ConsoleController * c; + ui::Textbox * commandField; + std::vector<ui::Label*> commandList; +public: + ConsoleView(); + virtual void OnDraw(); + virtual void DoKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + void AttachController(ConsoleController * c_) { c = c_; } + void NotifyPreviousCommandsChanged(ConsoleModel * sender); + void NotifyCurrentCommandChanged(ConsoleModel * sender); + virtual ~ConsoleView(); +}; + +#endif /* CONSOLEVIEW_H_ */ diff --git a/src/gui/dialogues/ConfirmPrompt.cpp b/src/gui/dialogues/ConfirmPrompt.cpp new file mode 100644 index 0000000..ba4060f --- /dev/null +++ b/src/gui/dialogues/ConfirmPrompt.cpp @@ -0,0 +1,152 @@ +#include "ConfirmPrompt.h" +#include "gui/Style.h" +#include "gui/interface/Label.h" +#include "gui/interface/Button.h" +#include "PowderToy.h" + +ConfirmPrompt::ConfirmPrompt(std::string title, std::string message, ConfirmDialogueCallback * callback_): + ui::Window(ui::Point(-1, -1), ui::Point(250, 35)), + callback(callback_) +{ + int width, height; + ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 15), title); + titleLabel->SetTextColour(style::Colour::WarningTitle); + titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(titleLabel); + + + ui::Label * messageLabel = new ui::Label(ui::Point(4, 25), ui::Point(Size.X-8, -1), message); + messageLabel->SetMultiline(true); + messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop; + AddComponent(messageLabel); + + Size.Y += messageLabel->Size.Y+12; + Position.Y = (ui::Engine::Ref().GetHeight()-Size.Y)/2; + + class CloseAction: public ui::ButtonAction + { + public: + ConfirmPrompt * prompt; + DialogueResult result; + CloseAction(ConfirmPrompt * prompt_, DialogueResult result_) { prompt = prompt_; result = result_; } + void ActionCallback(ui::Button * sender) + { + ui::Engine::Ref().CloseWindow(); + if(prompt->callback) + prompt->callback->ConfirmCallback(result); + prompt->SelfDestruct(); + } + }; + + + ui::Button * cancelButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X-75, 16), "Cancel"); + cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + cancelButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + cancelButton->Appearance.BorderInactive = ui::Colour(200, 200, 200); + cancelButton->SetActionCallback(new CloseAction(this, ResultCancel)); + AddComponent(cancelButton); + SetCancelButton(cancelButton); + + ui::Button * okayButton = new ui::Button(ui::Point(Size.X-76, Size.Y-16), ui::Point(76, 16), "Continue"); + okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + okayButton->Appearance.TextInactive = style::Colour::WarningTitle; + okayButton->SetActionCallback(new CloseAction(this, ResultOkay)); + AddComponent(okayButton); + SetOkayButton(okayButton); + + ui::Engine::Ref().ShowWindow(this); +} + +ConfirmPrompt::ConfirmPrompt(std::string title, std::string message, std::string buttonText, ConfirmDialogueCallback * callback_): + ui::Window(ui::Point(-1, -1), ui::Point(250, 50)), + callback(callback_) +{ + int width, height; + ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 15), title); + titleLabel->SetTextColour(style::Colour::WarningTitle); + titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(titleLabel); + + + ui::Label * messageLabel = new ui::Label(ui::Point(4, 25), ui::Point(Size.X-8, -1), message); + messageLabel->SetMultiline(true); + messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop; + AddComponent(messageLabel); + + Size.Y += messageLabel->Size.Y+12; + Position.Y = (ui::Engine::Ref().GetHeight()-Size.Y)/2; + + class CloseAction: public ui::ButtonAction + { + public: + ConfirmPrompt * prompt; + DialogueResult result; + CloseAction(ConfirmPrompt * prompt_, DialogueResult result_) { prompt = prompt_; result = result_; } + void ActionCallback(ui::Button * sender) + { + ui::Engine::Ref().CloseWindow(); + if(prompt->callback) + prompt->callback->ConfirmCallback(result); + prompt->SelfDestruct(); + } + }; + + + ui::Button * cancelButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X-75, 16), "Cancel"); + cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + cancelButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + cancelButton->Appearance.BorderInactive = ui::Colour(200, 200, 200); + cancelButton->SetActionCallback(new CloseAction(this, ResultCancel)); + AddComponent(cancelButton); + SetCancelButton(cancelButton); + + ui::Button * okayButton = new ui::Button(ui::Point(Size.X-76, Size.Y-16), ui::Point(76, 16), buttonText); + okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + okayButton->Appearance.TextInactive = style::Colour::WarningTitle; + okayButton->SetActionCallback(new CloseAction(this, ResultOkay)); + AddComponent(okayButton); + SetOkayButton(okayButton); + + ui::Engine::Ref().ShowWindow(this); +} + +bool ConfirmPrompt::Blocking(std::string title, std::string message, std::string buttonText) +{ + class BlockingPromptCallback: public ConfirmDialogueCallback { + public: + bool & outputResult; + BlockingPromptCallback(bool & output): outputResult(output) {} + virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) { + if (result == ConfirmPrompt::ResultOkay) + outputResult = true; + else + outputResult = false; + ui::Engine::Ref().Break(); + } + virtual ~BlockingPromptCallback() { } + }; + bool result; + new ConfirmPrompt(title, message, buttonText, new BlockingPromptCallback(result)); + EngineProcess(); + return result; +} + +void ConfirmPrompt::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + + g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255); +} + +ConfirmPrompt::~ConfirmPrompt() { + if(callback) + delete callback; +} + diff --git a/src/gui/dialogues/ConfirmPrompt.h b/src/gui/dialogues/ConfirmPrompt.h new file mode 100644 index 0000000..a2a3170 --- /dev/null +++ b/src/gui/dialogues/ConfirmPrompt.h @@ -0,0 +1,25 @@ +#ifndef CONFIRMPROMPT_H_ +#define CONFIRMPROMPT_H_ + +#include "gui/interface/Window.h" + +class ConfirmDialogueCallback; +class ConfirmPrompt: public ui::Window { +public: + enum DialogueResult { ResultCancel, ResultOkay }; + ConfirmPrompt(std::string title, std::string message, ConfirmDialogueCallback * callback_ = NULL); + ConfirmPrompt(std::string title, std::string message, std::string buttonText, ConfirmDialogueCallback * callback_ = NULL); + static bool Blocking(std::string title, std::string message, std::string buttonText = "Confirm"); + virtual void OnDraw(); + virtual ~ConfirmPrompt(); + ConfirmDialogueCallback * callback; +}; + +class ConfirmDialogueCallback +{ + public: + virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) {} + virtual ~ConfirmDialogueCallback() {} +}; + +#endif /* CONFIRMPROMPT_H_ */ diff --git a/src/gui/dialogues/ErrorMessage.cpp b/src/gui/dialogues/ErrorMessage.cpp new file mode 100644 index 0000000..b793aee --- /dev/null +++ b/src/gui/dialogues/ErrorMessage.cpp @@ -0,0 +1,78 @@ +#include "gui/Style.h" +#include "ErrorMessage.h" +#include "gui/interface/Button.h" +#include "gui/interface/Label.h" +#include "PowderToy.h" + +ErrorMessage::ErrorMessage(std::string title, std::string message, ErrorMessageCallback * callback_): + ui::Window(ui::Point(-1, -1), ui::Point(200, 35)), + callback(callback_) +{ + ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 16), title); + titleLabel->SetTextColour(style::Colour::ErrorTitle); + titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(titleLabel); + + ui::Label * messageLabel = new ui::Label(ui::Point(4, 24), ui::Point(Size.X-8, -1), message); + messageLabel->SetMultiline(true); + messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop; + AddComponent(messageLabel); + + Size.Y += messageLabel->Size.Y+12; + Position.Y = (ui::Engine::Ref().GetHeight()-Size.Y)/2; + + class DismissAction: public ui::ButtonAction + { + ErrorMessage * message; + public: + DismissAction(ErrorMessage * message_) { message = message_; } + void ActionCallback(ui::Button * sender) + { + ui::Engine::Ref().CloseWindow(); + if(message->callback) + message->callback->DismissCallback(); + message->SelfDestruct(); + } + }; + + ui::Button * okayButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "Dismiss"); + okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + okayButton->Appearance.BorderInactive = ui::Colour(200, 200, 200); + okayButton->SetActionCallback(new DismissAction(this)); + AddComponent(okayButton); + SetOkayButton(okayButton); + SetCancelButton(okayButton); + + ui::Engine::Ref().ShowWindow(this); +} + +void ErrorMessage::Blocking(std::string title, std::string message) +{ + class BlockingDismissCallback: public ErrorMessageCallback { + public: + BlockingDismissCallback() {} + virtual void DismissCallback() { + ui::Engine::Ref().Break(); + } + virtual ~BlockingDismissCallback() { } + }; + new ErrorMessage(title, message, new BlockingDismissCallback()); + EngineProcess(); +} + +void ErrorMessage::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + + g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255); +} + +ErrorMessage::~ErrorMessage() { + if(callback) + delete callback; +} + diff --git a/src/gui/dialogues/ErrorMessage.h b/src/gui/dialogues/ErrorMessage.h new file mode 100644 index 0000000..a7281b2 --- /dev/null +++ b/src/gui/dialogues/ErrorMessage.h @@ -0,0 +1,23 @@ +#ifndef ERRORMESSAGE_H_ +#define ERRORMESSAGE_H_ + +#include "gui/interface/Window.h" + +class ErrorMessageCallback; +class ErrorMessage: public ui::Window { + ErrorMessageCallback * callback; +public: + ErrorMessage(std::string title, std::string message, ErrorMessageCallback * callback_ = NULL); + static void Blocking(std::string title, std::string message); + virtual void OnDraw(); + virtual ~ErrorMessage(); +}; + +class ErrorMessageCallback +{ + public: + virtual void DismissCallback() {} + virtual ~ErrorMessageCallback() {} +}; + +#endif /* ERRORMESSAGE_H_ */ diff --git a/src/gui/dialogues/InformationMessage.cpp b/src/gui/dialogues/InformationMessage.cpp new file mode 100644 index 0000000..23b92dc --- /dev/null +++ b/src/gui/dialogues/InformationMessage.cpp @@ -0,0 +1,77 @@ +#include "gui/Style.h" +#include "InformationMessage.h" +#include "gui/interface/Button.h" +#include "gui/interface/Label.h" +#include "gui/interface/ScrollPanel.h" + +InformationMessage::InformationMessage(std::string title, std::string message, bool large): + ui::Window(ui::Point(-1, -1), ui::Point(200, 75)) +{ + if (large) //Maybe also use this large mode for changelogs eventually, or have it as a customizable size? + { + Size.X += 200; + Size.Y += 175; + } + + if (large) + { + ui::ScrollPanel *messagePanel = new ui::ScrollPanel(ui::Point(4, 24), ui::Point(Size.X-8, 206)); + AddComponent(messagePanel); + + ui::Label * messageLabel = new ui::Label(ui::Point(4, 0), ui::Point(Size.X-28, -1), message); + messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop; + messageLabel->SetMultiline(true); + messagePanel->AddChild(messageLabel); + + messagePanel->InnerSize = ui::Point(messagePanel->Size.X, messageLabel->Size.Y+4); + } + else + { + ui::Label * messageLabel = new ui::Label(ui::Point(4, 24), ui::Point(Size.X-8, 60), message); + messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop; + AddComponent(messageLabel); + } + + ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 16), title); + titleLabel->SetTextColour(style::Colour::InformationTitle); + titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(titleLabel); + + class DismissAction: public ui::ButtonAction + { + InformationMessage * message; + public: + DismissAction(InformationMessage * message_) { message = message_; } + void ActionCallback(ui::Button * sender) + { + ui::Engine::Ref().CloseWindow(); + message->SelfDestruct(); //TODO: Fix component disposal + } + }; + + ui::Button * okayButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "Dismiss"); + okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + okayButton->Appearance.BorderInactive = ui::Colour(200, 200, 200); + okayButton->SetActionCallback(new DismissAction(this)); + AddComponent(okayButton); + SetOkayButton(okayButton); + SetCancelButton(okayButton); + + ui::Engine::Ref().ShowWindow(this); +} + +void InformationMessage::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + + g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255); +} + +InformationMessage::~InformationMessage() { +} + diff --git a/src/gui/dialogues/InformationMessage.h b/src/gui/dialogues/InformationMessage.h new file mode 100644 index 0000000..6a26b38 --- /dev/null +++ b/src/gui/dialogues/InformationMessage.h @@ -0,0 +1,13 @@ +#ifndef INFORMATIONMESSAGE_H_ +#define INFORMATIONMESSAGE_H_ + +#include "gui/interface/Window.h" + +class InformationMessage: public ui::Window { +public: + InformationMessage(std::string title, std::string message, bool large); + virtual void OnDraw(); + virtual ~InformationMessage(); +}; + +#endif /* INFORMATIONMESSAGE_H_ */ diff --git a/src/gui/dialogues/LegacyDialogues.h b/src/gui/dialogues/LegacyDialogues.h new file mode 100644 index 0000000..7f6097d --- /dev/null +++ b/src/gui/dialogues/LegacyDialogues.h @@ -0,0 +1,11 @@ +#pragma once + +//Legacy blocking prompts +//This are not implemented here, but rather in the engine bootstrapper +bool ConfirmUI(std::string title, std::string message, std::string confirmText) {} + +void ErrorUI(std::string title, std::string message) {} + +void InformationUI(std::string title, std::string message) {} + +std::string MessagePromptUI(std::string title, std::string message, std::string text, std::string placeholder) {}
\ No newline at end of file diff --git a/src/gui/dialogues/TextPrompt.cpp b/src/gui/dialogues/TextPrompt.cpp new file mode 100644 index 0000000..92539ca --- /dev/null +++ b/src/gui/dialogues/TextPrompt.cpp @@ -0,0 +1,114 @@ +#include <iostream> +#include "TextPrompt.h" +#include "gui/interface/Label.h" +#include "gui/interface/Button.h" +#include "gui/Style.h" +#include "PowderToy.h" + +class CloseAction: public ui::ButtonAction +{ +public: + TextPrompt * prompt; + TextPrompt::DialogueResult result; + CloseAction(TextPrompt * prompt_, TextPrompt::DialogueResult result_) { prompt = prompt_; result = result_; } + void ActionCallback(ui::Button * sender) + { + ui::Engine::Ref().CloseWindow(); + if(prompt->callback) + prompt->callback->TextCallback(result, prompt->textField->GetText()); + prompt->SelfDestruct(); //TODO: Fix component disposal + } +}; + +TextPrompt::TextPrompt(std::string title, std::string message, std::string text, std::string placeholder, bool multiline, TextDialogueCallback * callback_): + ui::Window(ui::Point(-1, -1), ui::Point(200, 65)), + callback(callback_) +{ + if(multiline) + Size.X += 100; + + ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 18), title); + titleLabel->SetTextColour(style::Colour::WarningTitle); + titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(titleLabel); + + ui::Label * messageLabel = new ui::Label(ui::Point(4, 25), ui::Point(Size.X-8, -1), message); + messageLabel->SetMultiline(true); + messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop; + AddComponent(messageLabel); + + Size.Y += messageLabel->Size.Y+4; + + textField = new ui::Textbox(ui::Point(4, messageLabel->Position.Y + messageLabel->Size.Y + 7), ui::Point(Size.X-8, 16), text, placeholder); + if(multiline) + { + textField->SetMultiline(true); + textField->Size.Y = 60; + Size.Y += 45; + textField->Appearance.VerticalAlign = ui::Appearance::AlignTop; + } + else + { + textField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + } + textField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + AddComponent(textField); + FocusComponent(textField); + + ui::Button * cancelButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point((Size.X/2)+1, 16), "Cancel"); + cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + cancelButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + cancelButton->Appearance.BorderInactive = ui::Colour(200, 200, 200); + cancelButton->SetActionCallback(new CloseAction(this, ResultCancel)); + AddComponent(cancelButton); + SetCancelButton(cancelButton); + + ui::Button * okayButton = new ui::Button(ui::Point(Size.X/2, Size.Y-16), ui::Point(Size.X/2, 16), "Okay"); + okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignRight; + okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + okayButton->Appearance.TextInactive = style::Colour::WarningTitle; + okayButton->SetActionCallback(new CloseAction(this, ResultOkay)); + AddComponent(okayButton); + SetOkayButton(okayButton); + + ui::Engine::Ref().ShowWindow(this); +} + +std::string TextPrompt::Blocking(std::string title, std::string message, std::string text, std::string placeholder, bool multiline) +{ + std::string returnString = ""; + + class BlockingTextCallback: public TextDialogueCallback { + std::string & outputString; + public: + BlockingTextCallback(std::string & output) : outputString(output) {} + virtual void TextCallback(TextPrompt::DialogueResult result, std::string resultText) { + if(result == ResultOkay) + outputString = resultText; + else + outputString = ""; + ui::Engine::Ref().Break(); + } + virtual ~BlockingTextCallback() { } + }; + new TextPrompt(title, message, text, placeholder, multiline, new BlockingTextCallback(returnString)); + EngineProcess(); + + return returnString; +} + +void TextPrompt::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + + g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255); +} + +TextPrompt::~TextPrompt() { + if(callback) + delete callback; +} + diff --git a/src/gui/dialogues/TextPrompt.h b/src/gui/dialogues/TextPrompt.h new file mode 100644 index 0000000..e88c977 --- /dev/null +++ b/src/gui/dialogues/TextPrompt.h @@ -0,0 +1,28 @@ +#ifndef TEXTPROMPT_H_ +#define TEXTPROMPT_H_ + +#include "gui/interface/Window.h" +#include "gui/interface/Textbox.h" + +class TextDialogueCallback; +class TextPrompt: public ui::Window { +protected: + ui::Textbox * textField; +public: + friend class CloseAction; + enum DialogueResult { ResultCancel, ResultOkay }; + TextPrompt(std::string title, std::string message, std::string text, std::string placeholder, bool multiline, TextDialogueCallback * callback_); + static std::string Blocking(std::string title, std::string message, std::string text, std::string placeholder, bool multiline); + virtual void OnDraw(); + virtual ~TextPrompt(); + TextDialogueCallback * callback; +}; + +class TextDialogueCallback +{ + public: + virtual void TextCallback(TextPrompt::DialogueResult result, std::string resultText) {} + virtual ~TextDialogueCallback() {} +}; + +#endif /* TEXTPROMPT_H_ */ diff --git a/src/gui/elementsearch/ElementSearchActivity.cpp b/src/gui/elementsearch/ElementSearchActivity.cpp new file mode 100644 index 0000000..624f0ff --- /dev/null +++ b/src/gui/elementsearch/ElementSearchActivity.cpp @@ -0,0 +1,184 @@ +#include <algorithm> +#include "ElementSearchActivity.h" +#include "gui/interface/Textbox.h" +#include "gui/interface/Label.h" +#include "gui/interface/Keys.h" +#include "gui/game/Tool.h" +#include "gui/Style.h" +#include "gui/game/GameModel.h" + +class ElementSearchActivity::ToolAction: public ui::ButtonAction +{ + ElementSearchActivity * a; +public: + Tool * tool; + ToolAction(ElementSearchActivity * a, Tool * tool) : a(a), tool(tool) { } + void ActionCallback(ui::Button * sender_) + { + ToolButton *sender = (ToolButton*)sender_; + if(sender->GetSelectionState() >= 0 && sender->GetSelectionState() <= 2) + a->SetActiveTool(sender->GetSelectionState(), tool); + } +}; + +ElementSearchActivity::ElementSearchActivity(GameModel * gameModel, std::vector<Tool*> tools) : + WindowActivity(ui::Point(-1, -1), ui::Point(236, 302)), + gameModel(gameModel), + tools(tools), + firstResult(NULL) +{ + ui::Label * title = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 15), "Element Search"); + title->SetTextColour(style::Colour::InformationTitle); + title->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + AddComponent(title); + + class SearchAction : public ui::TextboxAction + { + private: + ElementSearchActivity * a; + public: + SearchAction(ElementSearchActivity * a) : a(a) {} + virtual void TextChangedCallback(ui::Textbox * sender) { + a->searchTools(sender->GetText()); + } + }; + + searchField = new ui::Textbox(ui::Point(8, 23), ui::Point(Size.X-16, 17), ""); + searchField->SetActionCallback(new SearchAction(this)); + searchField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + AddComponent(searchField); + FocusComponent(searchField); + + class CloseAction: public ui::ButtonAction + { + ElementSearchActivity * a; + public: + CloseAction(ElementSearchActivity * a) : a(a) { } + void ActionCallback(ui::Button * sender_) + { + a->Exit(); + } + }; + + class OKAction: public ui::ButtonAction + { + ElementSearchActivity * a; + public: + OKAction(ElementSearchActivity * a) : a(a) { } + void ActionCallback(ui::Button * sender_) + { + if(a->GetFirstResult()) + a->SetActiveTool(0, a->GetFirstResult()); + } + }; + + ui::Button * closeButton = new ui::Button(ui::Point(0, Size.Y-15), ui::Point((Size.X/2)+1, 15), "Close"); + closeButton->SetActionCallback(new CloseAction(this)); + ui::Button * okButton = new ui::Button(ui::Point(Size.X/2, Size.Y-15), ui::Point(Size.X/2, 15), "OK"); + okButton->SetActionCallback(new OKAction(this)); + + AddComponent(okButton); + AddComponent(closeButton); + + searchTools(""); +} + +void ElementSearchActivity::searchTools(std::string query) +{ + firstResult = NULL; + for(std::vector<ToolButton*>::iterator iter = toolButtons.begin(), end = toolButtons.end(); iter != end; ++iter) { + delete *iter; + RemoveComponent(*iter); + } + toolButtons.clear(); + + ui::Point viewPosition = searchField->Position + ui::Point(2+0, searchField->Size.Y+2+8); + ui::Point current = ui::Point(0, 0); + + std::string queryLower = std::string(query); + std::transform(queryLower.begin(), queryLower.end(), queryLower.begin(), ::tolower); + + for(std::vector<Tool*>::iterator iter = tools.begin(), end = tools.end(); iter != end; ++iter) { + std::string nameLower = std::string((*iter)->GetName()); + std::transform(nameLower.begin(), nameLower.end(), nameLower.begin(), ::tolower); + + if(strstr(nameLower.c_str(), queryLower.c_str())!=0) + { + Tool * tool = *iter; + + if(!firstResult) + firstResult = tool; + + VideoBuffer * tempTexture = tool->GetTexture(26, 14); + ToolButton * tempButton; + + if(tempTexture) + tempButton = new ToolButton(current+viewPosition, ui::Point(30, 18), "", tool->GetDescription()); + else + tempButton = new ToolButton(current+viewPosition, ui::Point(30, 18), tool->GetName(), tool->GetDescription()); + + tempButton->Appearance.SetTexture(tempTexture); + tempButton->Appearance.BackgroundInactive = ui::Colour(tool->colRed, tool->colGreen, tool->colBlue); + tempButton->SetActionCallback(new ToolAction(this, tool)); + + if(gameModel->GetActiveTool(0) == tool) + { + tempButton->SetSelectionState(0); //Primary + } + else if(gameModel->GetActiveTool(1) == tool) + { + tempButton->SetSelectionState(1); //Secondary + } + else if(gameModel->GetActiveTool(2) == tool) + { + tempButton->SetSelectionState(2); //Tertiary + } + + toolButtons.push_back(tempButton); + AddComponent(tempButton); + + current.X += 31; + + if(current.X + 30 > searchField->Size.X) { + current.X = 0; + current.Y += 19; + } + + if(current.Y + viewPosition.Y + 18 > Size.Y-23) + break; + } + } +} + +void ElementSearchActivity::SetActiveTool(int selectionState, Tool * tool) +{ + gameModel->SetActiveTool(selectionState, tool); + Exit(); +} + +void ElementSearchActivity::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + + g->drawrect(Position.X+searchField->Position.X, Position.Y+searchField->Position.Y+searchField->Size.Y+8, searchField->Size.X, Size.Y-(searchField->Position.Y+searchField->Size.Y+8)-23, 255, 255, 255, 180); +} + +void ElementSearchActivity::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + if(key == KEY_ENTER || key == KEY_RETURN) + { + if(firstResult) + gameModel->SetActiveTool(0, firstResult); + Exit(); + } + if(key == KEY_ESCAPE) + { + Exit(); + } +} + +ElementSearchActivity::~ElementSearchActivity() { +} + diff --git a/src/gui/elementsearch/ElementSearchActivity.h b/src/gui/elementsearch/ElementSearchActivity.h new file mode 100644 index 0000000..dec37e5 --- /dev/null +++ b/src/gui/elementsearch/ElementSearchActivity.h @@ -0,0 +1,32 @@ +#ifndef ELEMENTSEARCHACTIVITY_H_ +#define ELEMENTSEARCHACTIVITY_H_ + +#include <vector> +#include <string> +#include "Activity.h" +#include "gui/interface/Window.h" +#include "gui/interface/Textbox.h" +#include "gui/game/ToolButton.h" + +class Tool; + +class GameModel; + +class ElementSearchActivity: public WindowActivity { + Tool * firstResult; + GameModel * gameModel; + std::vector<Tool*> tools; + ui::Textbox * searchField; + std::vector<ToolButton*> toolButtons; + void searchTools(std::string query); +public: + class ToolAction; + Tool * GetFirstResult() { return firstResult; } + ElementSearchActivity(GameModel * gameModel, std::vector<Tool*> tools); + void SetActiveTool(int selectionState, Tool * tool); + virtual ~ElementSearchActivity(); + virtual void OnDraw(); + virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); +}; + +#endif /* ELEMENTSEARCHACTIVITY_H_ */ diff --git a/src/gui/filebrowser/FileBrowserActivity.cpp b/src/gui/filebrowser/FileBrowserActivity.cpp new file mode 100644 index 0000000..7612ae2 --- /dev/null +++ b/src/gui/filebrowser/FileBrowserActivity.cpp @@ -0,0 +1,336 @@ +#include <sstream> +#include <iostream> +#include "FileBrowserActivity.h" +#include "gui/interface/Label.h" +#include "gui/interface/Textbox.h" +#include "gui/interface/ScrollPanel.h" +#include "gui/interface/SaveButton.h" +#include "gui/interface/ProgressBar.h" +#include "client/Client.h" +#include "client/SaveFile.h" +#include "client/GameSave.h" +#include "gui/Style.h" +#include "tasks/Task.h" +#include "simulation/SaveRenderer.h" +#include "gui/dialogues/TextPrompt.h" +#include "gui/dialogues/ConfirmPrompt.h" +#include "gui/dialogues/ErrorMessage.h" + +class Thumbnail; + + +class SaveSelectedAction: public ui::SaveButtonAction +{ + FileBrowserActivity * a; +public: + SaveSelectedAction(FileBrowserActivity * _a) { a = _a; } + virtual void ActionCallback(ui::SaveButton * sender) + { + a->SelectSave(sender->GetSaveFile()); + } + virtual void AltActionCallback(ui::SaveButton * sender) + { + a->RenameSave(sender->GetSaveFile()); + } + virtual void AltActionCallback2(ui::SaveButton * sender) + { + a->DeleteSave(sender->GetSaveFile()); + } +}; + +//Currently, reading is done on another thread, we can't render outside the main thread due to some bullshit with OpenGL +class LoadFilesTask: public Task +{ + std::string directory; + std::string search; + std::vector<SaveFile*> saveFiles; + + virtual void before() + { + + } + + virtual void after() + { + + } + + virtual bool doWork() + { + std::vector<std::string> files = Client::Ref().DirectorySearch(directory, search, ".cps"); + + + notifyProgress(-1); + for(std::vector<std::string>::iterator iter = files.begin(), end = files.end(); iter != end; ++iter) + { + SaveFile * saveFile = new SaveFile(*iter); + try + { + std::vector<unsigned char> data = Client::Ref().ReadFile(*iter); + GameSave * tempSave = new GameSave(data); + saveFile->SetGameSave(tempSave); + saveFiles.push_back(saveFile); + + std::string filename = *iter; + size_t folderPos = filename.rfind(PATH_SEP); + if(folderPos!=std::string::npos && folderPos+1 < filename.size()) + { + filename = filename.substr(folderPos+1); + } + size_t extPos = filename.rfind("."); + if(extPos!=std::string::npos) + { + filename = filename.substr(0, extPos); + } + saveFile->SetDisplayName(filename); + } + catch(std::exception & e) + { + //:( + } + } + return true; + } + +public: + std::vector<SaveFile*> GetSaveFiles() + { + return saveFiles; + } + + LoadFilesTask(std::string directory, std::string search): + directory(directory), + search(search) + { + + } +}; + +class FileBrowserActivity::SearchAction: public ui::TextboxAction +{ +public: + FileBrowserActivity * a; + SearchAction(FileBrowserActivity * a) : a(a) {} + virtual void TextChangedCallback(ui::Textbox * sender) { + a->DoSearch(sender->GetText()); + } +}; + +FileBrowserActivity::FileBrowserActivity(std::string directory, FileSelectedCallback * callback): + WindowActivity(ui::Point(-1, -1), ui::Point(450, 300)), + callback(callback), + directory(directory), + totalFiles(0) +{ + + ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 18), "Save Browser"); + titleLabel->SetTextColour(style::Colour::WarningTitle); + titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(titleLabel); + + ui::Textbox * textField = new ui::Textbox(ui::Point(8, 25), ui::Point(Size.X-16, 16), "", "[search]"); + textField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + textField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + textField->SetActionCallback(new SearchAction(this)); + AddComponent(textField); + + itemList = new ui::ScrollPanel(ui::Point(4, 45), ui::Point(Size.X-8, Size.Y-53)); + AddComponent(itemList); + + progressBar = new ui::ProgressBar(ui::Point((Size.X-200)/2, 45+(Size.Y-66)/2), ui::Point(200, 17)); + AddComponent(progressBar); + + infoText = new ui::Label(ui::Point((Size.X-200)/2, 45+(Size.Y-66)/2), ui::Point(200, 17), "No saves found"); + AddComponent(infoText); + + filesX = 4; + filesY = 3; + buttonPadding = 2; + fileX = 0; + fileY = 0; + + buttonXOffset = 0; + buttonYOffset = 0; + buttonAreaWidth = itemList->Size.X; + buttonAreaHeight = itemList->Size.Y;// - buttonYOffset - 18; + buttonWidth = (buttonAreaWidth/filesX) - buttonPadding*2; + buttonHeight = (buttonAreaHeight/filesY) - buttonPadding*2; + + loadDirectory(directory, ""); +} + +void FileBrowserActivity::DoSearch(std::string search) +{ + if(!loadFiles) + { + loadDirectory(directory, search); + } +} + +void FileBrowserActivity::SelectSave(SaveFile * file) +{ + if(callback) + callback->FileSelected(new SaveFile(*file)); + Exit(); +} + +void FileBrowserActivity::DeleteSave(SaveFile * file) +{ + std::string deleteMessage = "Are you sure you want to delete " + file->GetDisplayName() + ".cps?"; + if (ConfirmPrompt::Blocking("Delete Save", deleteMessage)) + { + remove(file->GetName().c_str()); + loadDirectory(directory, ""); + } +} + +void FileBrowserActivity::RenameSave(SaveFile * file) +{ + std::string newName = TextPrompt::Blocking("Rename", "Change save name", file->GetDisplayName(), "", 0); + if (newName.length()) + { + newName = directory + PATH_SEP + newName + ".cps"; + int ret = rename(file->GetName().c_str(), newName.c_str()); + if (ret) + ErrorMessage::Blocking("Error", "Could not rename file"); + else + loadDirectory(directory, ""); + } + else + ErrorMessage::Blocking("Error", "No save name given"); +} + +void FileBrowserActivity::loadDirectory(std::string directory, std::string search) +{ + for(int i = 0; i < components.size(); i++) + { + RemoveComponent(components[i]); + itemList->RemoveChild(components[i]); + } + + for(std::vector<ui::Component*>::iterator iter = componentsQueue.begin(), end = componentsQueue.end(); iter != end; ++iter) + { + delete *iter; + } + componentsQueue.clear(); + + for(std::vector<SaveFile*>::iterator iter = files.begin(), end = files.end(); iter != end; ++iter) + { + delete *iter; + } + files.clear(); + + infoText->Visible = false; + progressBar->Visible = true; + progressBar->SetProgress(-1); + progressBar->SetStatus("Loading files"); + loadFiles = new LoadFilesTask(directory, search); + loadFiles->AddTaskListener(this); + loadFiles->Start(); +} + +void FileBrowserActivity::NotifyDone(Task * task) +{ + fileX = 0; + fileY = 0; + files = ((LoadFilesTask*)task)->GetSaveFiles(); + totalFiles = files.size(); + delete loadFiles; + loadFiles = NULL; + if(!files.size()) + { + progressBar->Visible = false; + infoText->Visible = true; + } + for(int i = 0; i < components.size(); i++) + { + delete components[i]; + } + components.clear(); +} + +void FileBrowserActivity::OnMouseDown(int x, int y, unsigned button) +{ + if(!(x > Position.X && y > Position.Y && y < Position.Y+Size.Y && x < Position.X+Size.X)) //Clicked outside window + Exit(); +} + +void FileBrowserActivity::OnTryExit(ExitMethod method) +{ + Exit(); +} + +void FileBrowserActivity::NotifyError(Task * task) +{ + +} + +void FileBrowserActivity::NotifyProgress(Task * task) +{ + progressBar->SetProgress(task->GetProgress()); +} + +void FileBrowserActivity::NotifyStatus(Task * task) +{ + +} + +void FileBrowserActivity::OnTick(float dt) +{ + if(loadFiles) + loadFiles->Poll(); + + if(files.size()) + { + SaveFile * saveFile = files.back(); + files.pop_back(); + + if(fileX == filesX) + { + fileX = 0; + fileY++; + } + ui::SaveButton * saveButton = new ui::SaveButton( + ui::Point( + buttonXOffset + buttonPadding + fileX*(buttonWidth+buttonPadding*2), + buttonYOffset + buttonPadding + fileY*(buttonHeight+buttonPadding*2) + ), + ui::Point(buttonWidth, buttonHeight), + saveFile); + saveButton->AddContextMenu(1); + saveButton->Tick(dt); + saveButton->SetActionCallback(new SaveSelectedAction(this)); + progressBar->SetStatus("Rendering thumbnails"); + progressBar->SetProgress((float(totalFiles-files.size())/float(totalFiles))*100.0f); + componentsQueue.push_back(saveButton); + fileX++; + } + else if(componentsQueue.size()) + { + for(std::vector<ui::Component*>::iterator iter = componentsQueue.begin(), end = componentsQueue.end(); iter != end; ++iter) + { + components.push_back(*iter); + itemList->AddChild(*iter); + } + componentsQueue.clear(); + itemList->InnerSize.Y = (buttonHeight+(buttonPadding*2))*fileY; + if(!componentsQueue.size()) + progressBar->Visible = false; + } +} + +void FileBrowserActivity::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + + //Window Background+Outline + g->clearrect(Position.X-2, Position.Y-2, Size.X+4, Size.Y+4); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); +} + +FileBrowserActivity::~FileBrowserActivity() +{ + if(callback) + delete callback; +}
\ No newline at end of file diff --git a/src/gui/filebrowser/FileBrowserActivity.h b/src/gui/filebrowser/FileBrowserActivity.h new file mode 100644 index 0000000..d607d49 --- /dev/null +++ b/src/gui/filebrowser/FileBrowserActivity.h @@ -0,0 +1,65 @@ +#pragma once + +#include <vector> +#include <string> +#include "Activity.h" +#include "gui/interface/Window.h" +#include "tasks/TaskListener.h" + + +class SaveFile; +class FileSelectedCallback +{ +public: + FileSelectedCallback() {} + virtual ~FileSelectedCallback() {} + virtual void FileSelected(SaveFile* file) {} +}; + +namespace ui +{ + class Label; + class ScrollPanel; + class ProgressBar; +} + +class LoadFilesTask; +class FileBrowserActivity: public TaskListener, public WindowActivity +{ + LoadFilesTask * loadFiles; + FileSelectedCallback * callback; + ui::ScrollPanel * itemList; + ui::Label * infoText; + std::vector<SaveFile*> files; + std::vector<ui::Component*> components; + std::vector<ui::Component*> componentsQueue; + std::string directory; + + ui::ProgressBar * progressBar; + + int totalFiles; + int filesX, filesY, buttonPadding; + int fileX, fileY; + int buttonWidth, buttonHeight, buttonAreaWidth, buttonAreaHeight, buttonXOffset, buttonYOffset; + + + class SearchAction; + void populateList(); +public: + FileBrowserActivity(std::string directory, FileSelectedCallback * callback); + virtual void OnDraw(); + virtual void OnTick(float dt); + virtual void OnTryExit(ExitMethod method); + virtual void OnMouseDown(int x, int y, unsigned button); + void loadDirectory(std::string directory, std::string search); + void SelectSave(SaveFile * file); + void DeleteSave(SaveFile * file); + void RenameSave(SaveFile * file); + void DoSearch(std::string search); + virtual ~FileBrowserActivity(); + + virtual void NotifyDone(Task * task); + virtual void NotifyError(Task * task); + virtual void NotifyProgress(Task * task); + virtual void NotifyStatus(Task * task); +};
\ No newline at end of file diff --git a/src/gui/game/BitmapBrush.h b/src/gui/game/BitmapBrush.h new file mode 100644 index 0000000..e1c0445 --- /dev/null +++ b/src/gui/game/BitmapBrush.h @@ -0,0 +1,96 @@ +/* + * BitmapBrush.h + * + * Created on: Nov 18, 2012 + * Author: Simon Robertshaw + */ + +#ifndef BTIMAPBRUSH_H_ +#define BTIMAPBRUSH_H_ + +#include <vector> +#include <cmath> +#include "Brush.h" + +class BitmapBrush: public Brush +{ +protected: + ui::Point origSize; + unsigned char * origBitmap; +public: + BitmapBrush(std::vector<unsigned char> newBitmap, ui::Point rectSize_): + Brush(ui::Point(0, 0)), + origSize(0, 0) + { + ui::Point newSize = rectSize_; + + //Ensure the rect has odd dimentions so we can pull an integer radius with a 1x1 centre + if(!(newSize.X % 2)) + newSize.X += 1; + if(!(newSize.Y % 2)) + newSize.Y += 1; + + radius = (newSize-ui::Point(1, 1))/2; + size = newSize; + origSize = size; + + origBitmap = new unsigned char[size.X*size.Y]; + std::fill(origBitmap, origBitmap+(size.X*size.Y), 0); + for(int y = 0; y < rectSize_.Y; y++) + { + for(int x = 0; x < rectSize_.X; x++) + { + if(newBitmap[(y*rectSize_.X)+x] >= 128) + origBitmap[(y*size.X)+x] = newBitmap[(y*rectSize_.X)+x]; + } + } + + SetRadius(radius); + }; + virtual void GenerateBitmap() + { + if(origBitmap) + { + if(bitmap) + delete[] bitmap; + bitmap = new unsigned char[size.X*size.Y]; + if(size == origSize) + std::copy(origBitmap, origBitmap+(origSize.X*origSize.Y), bitmap); + else + { + //Bilinear interpolation + float factorX = ((float)origSize.X)/((float)size.X); + float factorY = ((float)origSize.Y)/((float)size.Y); + for(int y = 0; y < size.Y; y++) + { + for(int x = 0; x < size.X; x++) + { + float originalY = ((float)y)*factorY; + float originalX = ((float)x)*factorX; + + int lowerX = std::floor(originalX); + int upperX = std::min((float)(origSize.X-1), std::floor(originalX+1.0f)); + int lowerY = std::floor(originalY); + int upperY = std::min((float)(origSize.Y-1), std::floor(originalY+1.0f)); + + unsigned char topRight = origBitmap[(lowerY*origSize.X)+upperX]; + unsigned char topLeft = origBitmap[(lowerY*origSize.X)+lowerX]; + unsigned char bottomRight = origBitmap[(upperY*origSize.X)+upperX]; + unsigned char bottomLeft = origBitmap[(upperY*origSize.X)+lowerX]; + float top = LinearInterpolate<float>(topLeft, topRight, lowerX, upperX, originalX); + float bottom = LinearInterpolate<float>(bottomLeft, bottomRight, lowerX, upperX, originalX); + float mid = LinearInterpolate<float>(top, bottom, lowerY, upperY, originalY); + bitmap[(y*size.X)+x] = mid > 128 ? 255 : 0; + } + } + } + } + } + virtual ~BitmapBrush() + { + if(origBitmap) + delete[] origBitmap; + } +}; + +#endif /* BTIMAPBRUSH_H_ */ diff --git a/src/gui/game/Brush.cpp b/src/gui/game/Brush.cpp new file mode 100644 index 0000000..9f2f2a8 --- /dev/null +++ b/src/gui/game/Brush.cpp @@ -0,0 +1,51 @@ +#include "Brush.h" +#include "graphics/Renderer.h" + +void Brush::RenderRect(Renderer * ren, ui::Point position1, ui::Point position2) +{ + int width, height, t; + width = position2.X-position1.X; + height = position2.Y-position1.Y; + if(height<0) + { + position1.Y += height; + height *= -1; + } + if(width<0) + { + position1.X += width; + width *= -1; + } + + ren->xor_line(position1.X, position1.Y, position1.X+width, position1.Y); + if(height>0){ + ren->xor_line(position1.X, position1.Y+height, position1.X+width, position1.Y+height); + if(height>1){ + ren->xor_line(position1.X+width, position1.Y+1, position1.X+width, position1.Y+height-1); + if(width>0) + ren->xor_line(position1.X, position1.Y+1, position1.X, position1.Y+height-1); + } + } +} + +void Brush::RenderLine(Renderer * ren, ui::Point position1, ui::Point position2) +{ + ren->xor_line(position1.X, position1.Y, position2.X, position2.Y); +} + +void Brush::RenderPoint(Renderer * ren, ui::Point position) +{ + if(!outline) + updateOutline(); + if(!outline) + return; + ren->xor_bitmap(outline, position.X-radius.X, position.Y-radius.Y, size.X, size.Y); +} + +void Brush::RenderFill(Renderer * ren, ui::Point position) +{ + ren->xor_line(position.X-5, position.Y, position.X-1, position.Y); + ren->xor_line(position.X+5, position.Y, position.X+1, position.Y); + ren->xor_line(position.X, position.Y-5, position.X, position.Y-1); + ren->xor_line(position.X, position.Y+5, position.X, position.Y+1); +} diff --git a/src/gui/game/Brush.h b/src/gui/game/Brush.h new file mode 100644 index 0000000..a853609 --- /dev/null +++ b/src/gui/game/Brush.h @@ -0,0 +1,108 @@ +#ifndef BRUSH_H_ +#define BRUSH_H_ + +#include <iostream> +#include "gui/interface/Point.h" + +class Renderer; +class Brush +{ +protected: + unsigned char * outline; + unsigned char * bitmap; + ui::Point size; + ui::Point radius; + void updateOutline() + { + if(!bitmap) + GenerateBitmap(); + if(!bitmap) + return; + if(outline) + delete[] outline; + outline = new unsigned char[size.X*size.Y]; + for(int x = 0; x < size.X; x++) + { + for(int y = 0; y < size.Y; y++) + { + if(bitmap[y*size.X+x] && (!y || !x || x == size.X-1 || y == size.Y-1 || !bitmap[y*size.X+(x+1)] || !bitmap[y*size.X+(x-1)] || !bitmap[(y-1)*size.X+x] || !bitmap[(y+1)*size.X+x])) + { + outline[y*size.X+x] = 255; + } + else + outline[y*size.X+x] = 0; + } + } + } +public: + Brush(ui::Point size_): + bitmap(NULL), + outline(NULL), + radius(0, 0), + size(0, 0) + { + SetRadius(size_); + }; + + //Radius of the brush 0x0 - infxinf (Radius of 0x0 would be 1x1, radius of 1x1 would be 3x3) + ui::Point GetRadius() + { + return radius; + } + + //Size of the brush bitmap mask, 1x1 - infxinf + ui::Point GetSize() + { + return size; + } + virtual void SetRadius(ui::Point radius) + { + this->radius = radius; + this->size = radius+radius+ui::Point(1, 1); + + GenerateBitmap(); + updateOutline(); + } + virtual ~Brush() { + if(bitmap) + delete[] bitmap; + if(outline) + delete[] outline; + } + virtual void RenderRect(Renderer * ren, ui::Point position1, ui::Point position2); + virtual void RenderLine(Renderer * ren, ui::Point position1, ui::Point position2); + virtual void RenderPoint(Renderer * ren, ui::Point position); + virtual void RenderFill(Renderer * ren, ui::Point position); + virtual void GenerateBitmap() + { + if(bitmap) + delete[] bitmap; + bitmap = new unsigned char[size.X*size.Y]; + for(int x = 0; x < size.X; x++) + { + for(int y = 0; y < size.Y; y++) + { + bitmap[(y*size.X)+x] = 255; + } + } + } + //Get a bitmap for drawing particles + unsigned char * GetBitmap() + { + if(!bitmap) + GenerateBitmap(); + return bitmap; + } + + unsigned char * GetOutline() + { + if(!outline) + updateOutline(); + if(!outline) + return NULL; + return outline; + } +}; + + +#endif /* BRUSH_H_ */ diff --git a/src/gui/game/DecorationTool.h b/src/gui/game/DecorationTool.h new file mode 100644 index 0000000..679c854 --- /dev/null +++ b/src/gui/game/DecorationTool.h @@ -0,0 +1,43 @@ + +#ifndef DECORATIONTOOL_H_ +#define DECORATIONTOOL_H_ + +#include "Tool.h" + +class DecorationTool: public Tool +{ +public: + enum ToolType { BlendAdd = DECO_ADD, BlendRemove = DECO_SUBTRACT, BlendMultiply = DECO_MULTIPLY, BlendDivide = DECO_DIVIDE, BlendSet = DECO_DRAW, BlendSmudge = DECO_SMUDGE, Remove = DECO_CLEAR }; + + ToolType decoMode; + + unsigned char Red; + unsigned char Green; + unsigned char Blue; + unsigned char Alpha; + + DecorationTool(ToolType decoMode_, string name, string description, int r, int g, int b, std::string identifier): + Tool(0, name, description, r, g, b, identifier), + decoMode(decoMode_), + Red(0), + Green(0), + Blue(0), + Alpha(0) + { + } + virtual ~DecorationTool() {} + virtual void Draw(Simulation * sim, Brush * brush, ui::Point position){ + sim->ApplyDecorationPoint(position.X, position.Y, Red, Green, Blue, Alpha, decoMode, brush); + } + virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) { + sim->ApplyDecorationLine(position1.X, position1.Y, position2.X, position2.Y, Red, Green, Blue, Alpha, decoMode, brush); + } + virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { + sim->ApplyDecorationBox(position1.X, position1.Y, position2.X, position2.Y, Red, Green, Blue, Alpha, decoMode); + } + virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position) { + + } +}; + +#endif diff --git a/src/gui/game/EllipseBrush.h b/src/gui/game/EllipseBrush.h new file mode 100644 index 0000000..ad28766 --- /dev/null +++ b/src/gui/game/EllipseBrush.h @@ -0,0 +1,56 @@ +#ifndef ELIPSEBRUSH_H_ +#define ELIPSEBRUSH_H_ + +#include <cmath> +#include "Brush.h" + +class EllipseBrush: public Brush +{ +public: + EllipseBrush(ui::Point size_): + Brush(size_) + { + SetRadius(size_); + }; + virtual void GenerateBitmap() + { + if(bitmap) + delete[] bitmap; + bitmap = new unsigned char[size.X*size.Y]; + int rx = radius.X; + int ry = radius.Y; + + if (!rx) + { + for (int j = 0; j <= 2*ry; j++) + { + bitmap[j*(size.X)+rx] = 255; + } + } + else + { + int yTop = ry, yBottom, i, j; + for (i = 0; i <= rx; i++) + { + while (pow(i-rx,2.0f)*pow(ry,2.0f) + pow(yTop-ry,2.0f)*pow(rx,2.0f) <= pow(rx,2.0f)*pow(ry,2.0f)) + yTop++; + yBottom = 2*ry - yTop; + for (int j = 0; j <= ry*2; j++) + { + if (j > yBottom && j < yTop) + { + bitmap[j*(size.X)+i] = 255; + bitmap[j*(size.X)+2*rx-i] = 255; + } + else + { + bitmap[j*(size.X)+i] = 0; + bitmap[j*(size.X)+2*rx-i] = 0; + } + } + } + } + } +}; + +#endif /* ELIPSEBRUSH_H_ */ diff --git a/src/gui/game/GameController.cpp b/src/gui/game/GameController.cpp new file mode 100644 index 0000000..8df5036 --- /dev/null +++ b/src/gui/game/GameController.cpp @@ -0,0 +1,1426 @@ + +#include <iostream> +#include <queue> +#include "Config.h" +#include "Format.h" +#include "GameController.h" +#include "GameModel.h" +#include "client/SaveInfo.h" +#include "client/GameSave.h" +#include "gui/search/SearchController.h" +#include "gui/render/RenderController.h" +#include "gui/login/LoginController.h" +#include "gui/interface/Point.h" +#include "gui/dialogues/ErrorMessage.h" +#include "gui/dialogues/InformationMessage.h" +#include "gui/dialogues/ConfirmPrompt.h" +#include "GameModelException.h" +#include "simulation/Air.h" +#include "gui/elementsearch/ElementSearchActivity.h" +#include "gui/profile/ProfileActivity.h" +#include "gui/colourpicker/ColourPickerActivity.h" +#include "gui/update/UpdateActivity.h" +#include "Notification.h" +#include "gui/filebrowser/FileBrowserActivity.h" +#include "gui/save/LocalSaveActivity.h" +#include "gui/save/ServerSaveActivity.h" +#include "gui/interface/Keys.h" +#include "simulation/Snapshot.h" +#include "debug/DebugInfo.h" +//#include "debug/ElementPopulation.h" + +using namespace std; + +class GameController::SearchCallback: public ControllerCallback +{ + GameController * cc; +public: + SearchCallback(GameController * cc_) { cc = cc_; } + virtual void ControllerExit() + { + if(cc->search->GetLoadedSave()) + { + try + { + cc->gameModel->SetSave(cc->search->GetLoadedSave()); + cc->search->ReleaseLoadedSave(); + } + catch(GameModelException & ex) + { + new ErrorMessage("Cannot open save", ex.what()); + } + } + } +}; + +class GameController::SaveOpenCallback: public ControllerCallback +{ + GameController * cc; +public: + SaveOpenCallback(GameController * cc_) { cc = cc_; } + virtual void ControllerExit() + { + if(cc->activePreview->GetDoOpen() && cc->activePreview->GetSave()) + { + try + { + cc->LoadSave(cc->activePreview->GetSave()); + } + catch(GameModelException & ex) + { + new ErrorMessage("Cannot open save", ex.what()); + } + } + } +}; + + +class GameController::RenderCallback: public ControllerCallback +{ + GameController * cc; +public: + RenderCallback(GameController * cc_) { cc = cc_; } + virtual void ControllerExit() + { + //cc->gameModel->SetUser(cc->loginWindow->GetUser()); + } +}; + +class GameController::OptionsCallback: public ControllerCallback +{ + GameController * cc; +public: + OptionsCallback(GameController * cc_) { cc = cc_; } + virtual void ControllerExit() + { + cc->gameModel->UpdateQuickOptions(); + //cc->gameModel->SetUser(cc->loginWindow->GetUser()); + } +}; + +class GameController::TagsCallback: public ControllerCallback +{ + GameController * cc; +public: + TagsCallback(GameController * cc_) { cc = cc_; } + virtual void ControllerExit() + { + cc->gameView->NotifySaveChanged(cc->gameModel); + } +}; + +class GameController::StampsCallback: public ControllerCallback +{ + GameController * cc; +public: + StampsCallback(GameController * cc_) { cc = cc_; } + virtual void ControllerExit() + { + if(cc->localBrowser->GetSave()) + { + cc->gameModel->SetStamp(cc->localBrowser->GetSave()->GetGameSave()); + if (cc->localBrowser->GetMoveToFront()) + Client::Ref().MoveStampToFront(cc->localBrowser->GetSave()->GetName()); + cc->LoadStamp(); + } + } +}; + +GameController::GameController(): + search(NULL), + renderOptions(NULL), + loginWindow(NULL), + console(NULL), + tagsWindow(NULL), + options(NULL), + activePreview(NULL), + localBrowser(NULL), + HasDone(false), + firstTick(true) +{ + gameView = new GameView(); + gameModel = new GameModel(); + gameModel->BuildQuickOptionMenu(this); + + gameView->AttachController(this); + gameModel->AddObserver(gameView); + + commandInterface = new LuaScriptInterface(this, gameModel);//new TPTScriptInterface(); + ((LuaScriptInterface*)commandInterface)->SetWindow(gameView); + + commandInterface->OnBrushChanged(gameModel->GetBrushID(), gameModel->GetBrush()->GetRadius().X, gameModel->GetBrush()->GetRadius().X); + ActiveToolChanged(0, gameModel->GetActiveTool(0)); + ActiveToolChanged(1, gameModel->GetActiveTool(1)); + ActiveToolChanged(2, gameModel->GetActiveTool(2)); + + //sim = new Simulation(); + Client::Ref().AddListener(this); + + //debugInfo.push_back(new ElementPopulationDebug(gameModel->GetSimulation())); +} + +GameController::~GameController() +{ + if(search) + { + delete search; + } + if(renderOptions) + { + delete renderOptions; + } + if(loginWindow) + { + delete loginWindow; + } + if(tagsWindow) + { + delete tagsWindow; + } + if(console) + { + delete console; + } + if(activePreview) + { + delete activePreview; + } + if(localBrowser) + { + delete localBrowser; + } + if (options) + { + delete options; + } + if(ui::Engine::Ref().GetWindow() == gameView) + { + ui::Engine::Ref().CloseWindow(); + } + //deleted here because it refuses to be deleted when deleted from gameModel even with the same code + std::deque<Snapshot*> history = gameModel->GetHistory(); + for(std::deque<Snapshot*>::iterator iter = history.begin(), end = history.end(); iter != end; ++iter) + { + delete *iter; + } + std::vector<QuickOption*> quickOptions = gameModel->GetQuickOptions(); + for(std::vector<QuickOption*>::iterator iter = quickOptions.begin(), end = quickOptions.end(); iter != end; ++iter) + { + delete *iter; + } + std::vector<Notification*> notifications = gameModel->GetNotifications(); + for(std::vector<Notification*>::iterator iter = notifications.begin(); iter != notifications.end(); ++iter) + { + delete *iter; + } + delete gameModel; + delete gameView; +} + +void GameController::HistoryRestore() +{ + std::deque<Snapshot*> history = gameModel->GetHistory(); + if(history.size()) + { + Snapshot * snap = history.back(); + gameModel->GetSimulation()->Restore(*snap); + if(history.size()>1) + { + history.pop_back(); + delete snap; + gameModel->SetHistory(history); + } + } +} + +void GameController::HistorySnapshot() +{ + std::deque<Snapshot*> history = gameModel->GetHistory(); + Snapshot * newSnap = gameModel->GetSimulation()->CreateSnapshot(); + if(newSnap) + { + if(history.size() >= 1) //History limit is current 1 + { + Snapshot * snap = history.front(); + history.pop_front(); + //snap->Particles.clear(); + delete snap; + } + history.push_back(newSnap); + gameModel->SetHistory(history); + } +} + +GameView * GameController::GetView() +{ + return gameView; +} + +void GameController::PlaceSave(ui::Point position) +{ + if(gameModel->GetPlaceSave()) + { + gameModel->GetSimulation()->Load(position.X, position.Y, gameModel->GetPlaceSave()); + gameModel->SetPaused(gameModel->GetPlaceSave()->paused | gameModel->GetPaused()); + } +} + +void GameController::Install() +{ +#if defined(MACOSX) + new InformationMessage("No Installation necessary", "You don't need to install The Powder Toy on Mac OS X", false); +#elif defined(WIN) || defined(LIN) + class InstallConfirmation: public ConfirmDialogueCallback { + public: + GameController * c; + InstallConfirmation(GameController * c_) { c = c_; } + virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) { + if (result == ConfirmPrompt::ResultOkay) + { + if(Client::Ref().DoInstallation()) + { + new InformationMessage("Install Success", "The installation completed!", false); + } + else + { + new ErrorMessage("Could not install", "The installation did not complete due to an error"); + } + } + } + virtual ~InstallConfirmation() { } + }; + new ConfirmPrompt("Install The Powder Toy", "Do you wish to install The Powder Toy on this computer?\nThis allows you to open save files and saves directly from the website.", new InstallConfirmation(this)); +#else + new ErrorMessage("Cannot install", "You cannot install The Powder Toy on this platform"); +#endif +} + +void GameController::AdjustGridSize(int direction) +{ + if(direction > 0) + gameModel->GetRenderer()->SetGridSize((gameModel->GetRenderer()->GetGridSize()+1)%10); + else + gameModel->GetRenderer()->SetGridSize((gameModel->GetRenderer()->GetGridSize()+9)%10); +} + +void GameController::InvertAirSim() +{ + gameModel->GetSimulation()->air->Invert(); +} + + +void GameController::AdjustBrushSize(int direction, bool logarithmic, bool xAxis, bool yAxis) +{ + if(xAxis && yAxis) + return; + + ui::Point newSize(0, 0); + ui::Point oldSize = gameModel->GetBrush()->GetRadius(); + if(logarithmic) + newSize = gameModel->GetBrush()->GetRadius() + ui::Point(direction * ((gameModel->GetBrush()->GetRadius().X/5)>0?gameModel->GetBrush()->GetRadius().X/5:1), direction * ((gameModel->GetBrush()->GetRadius().Y/5)>0?gameModel->GetBrush()->GetRadius().Y/5:1)); + else + newSize = gameModel->GetBrush()->GetRadius() + ui::Point(direction, direction); + if(newSize.X < 0) + newSize.X = 0; + if(newSize.Y < 0) + newSize.Y = 0; + if(newSize.X > 200) + newSize.X = 200; + if(newSize.Y > 200) + newSize.Y = 200; + + if(xAxis) + gameModel->GetBrush()->SetRadius(ui::Point(newSize.X, oldSize.Y)); + else if(yAxis) + gameModel->GetBrush()->SetRadius(ui::Point(oldSize.X, newSize.Y)); + else + gameModel->GetBrush()->SetRadius(newSize); + + BrushChanged(gameModel->GetBrushID(), gameModel->GetBrush()->GetRadius().X, gameModel->GetBrush()->GetRadius().Y); +} + +void GameController::AdjustZoomSize(int direction, bool logarithmic) +{ + int newSize; + if(logarithmic) + newSize = gameModel->GetZoomSize()+(((gameModel->GetZoomSize()/10)>0?(gameModel->GetZoomSize()/10):1)*direction); + else + newSize = gameModel->GetZoomSize()+direction; + if(newSize<5) + newSize = 5; + if(newSize>64) + newSize = 64; + gameModel->SetZoomSize(newSize); + + int newZoomFactor = 256/newSize; + if(newZoomFactor<3) + newZoomFactor = 3; + gameModel->SetZoomFactor(newZoomFactor); +} + +ui::Point GameController::PointTranslate(ui::Point point) +{ + if(point.X >= XRES) + point.X = XRES-1; + if(point.Y >= YRES) + point.Y = YRES-1; + if(point.Y < 0) + point.Y = 0; + if(point.X < 0) + point.X = 0; + + bool zoomEnabled = gameModel->GetZoomEnabled(); + if(!zoomEnabled) + return point; + //If we try to draw inside the zoom window, normalise the coordinates + int zoomFactor = gameModel->GetZoomFactor(); + ui::Point zoomWindowPosition = gameModel->GetZoomWindowPosition(); + ui::Point zoomWindowSize = ui::Point(gameModel->GetZoomSize()*zoomFactor, gameModel->GetZoomSize()*zoomFactor); + + if(point.X >= zoomWindowPosition.X && point.X >= zoomWindowPosition.Y && point.X <= zoomWindowPosition.X+zoomWindowSize.X && point.Y <= zoomWindowPosition.Y+zoomWindowSize.Y) + return ((point-zoomWindowPosition)/gameModel->GetZoomFactor())+gameModel->GetZoomPosition(); + return point; +} + +ui::Point GameController::NormaliseBlockCoord(ui::Point point) +{ + return (point/CELL)*CELL; +} + +void GameController::DrawRect(int toolSelection, ui::Point point1, ui::Point point2) +{ + Simulation * sim = gameModel->GetSimulation(); + Tool * activeTool = gameModel->GetActiveTool(toolSelection); + gameModel->SetLastTool(activeTool); + Brush * cBrush = gameModel->GetBrush(); + if(!activeTool || !cBrush) + return; + activeTool->SetStrength(gameModel->GetToolStrength()); + activeTool->DrawRect(sim, cBrush, point1, point2); +} + +void GameController::DrawLine(int toolSelection, ui::Point point1, ui::Point point2) +{ + Simulation * sim = gameModel->GetSimulation(); + Tool * activeTool = gameModel->GetActiveTool(toolSelection); + gameModel->SetLastTool(activeTool); + Brush * cBrush = gameModel->GetBrush(); + if(!activeTool || !cBrush) + return; + activeTool->SetStrength(gameModel->GetToolStrength()); + activeTool->DrawLine(sim, cBrush, point1, point2); +} + +void GameController::DrawFill(int toolSelection, ui::Point point) +{ + Simulation * sim = gameModel->GetSimulation(); + Tool * activeTool = gameModel->GetActiveTool(toolSelection); + gameModel->SetLastTool(activeTool); + Brush * cBrush = gameModel->GetBrush(); + if(!activeTool || !cBrush) + return; + activeTool->SetStrength(gameModel->GetToolStrength()); + activeTool->DrawFill(sim, cBrush, point); +} + +void GameController::DrawPoints(int toolSelection, queue<ui::Point> & pointQueue) +{ + Simulation * sim = gameModel->GetSimulation(); + Tool * activeTool = gameModel->GetActiveTool(toolSelection); + gameModel->SetLastTool(activeTool); + Brush * cBrush = gameModel->GetBrush(); + if(!activeTool || !cBrush) + { + if(!pointQueue.empty()) + { + while(!pointQueue.empty()) + { + //delete pointQueue.front(); + pointQueue.pop(); + } + } + return; + } + + activeTool->SetStrength(gameModel->GetToolStrength()); + if(!pointQueue.empty()) + { + ui::Point sPoint(0, 0); + bool first = true; + while(!pointQueue.empty()) + { + ui::Point fPoint = pointQueue.front(); + //delete pointQueue.front(); + pointQueue.pop(); + if(!first) + { + activeTool->DrawLine(sim, cBrush, sPoint, fPoint, true); + } + else + { + first = false; + activeTool->Draw(sim, cBrush, fPoint); + } + sPoint = fPoint; + } + } +} + +void GameController::LoadClipboard() +{ + gameModel->SetPlaceSave(gameModel->GetClipboard()); + if(gameModel->GetPlaceSave() && gameModel->GetPlaceSave()->Collapsed()) + gameModel->GetPlaceSave()->Expand(); +} + +void GameController::LoadStamp() +{ + gameModel->SetPlaceSave(gameModel->GetStamp()); + if(gameModel->GetPlaceSave() && gameModel->GetPlaceSave()->Collapsed()) + gameModel->GetPlaceSave()->Expand(); +} + +void GameController::TranslateSave(ui::Point point) +{ + matrix2d transform = m2d_identity; + vector2d translate = v2d_new(point.X, point.Y); + gameModel->GetPlaceSave()->Transform(transform, translate); + gameModel->SetPlaceSave(gameModel->GetPlaceSave()); +} + +void GameController::TransformSave(matrix2d transform) +{ + vector2d translate = v2d_zero; + gameModel->GetPlaceSave()->Transform(transform, translate); + gameModel->SetPlaceSave(gameModel->GetPlaceSave()); +} + +void GameController::ToolClick(int toolSelection, ui::Point point) +{ + Simulation * sim = gameModel->GetSimulation(); + Tool * activeTool = gameModel->GetActiveTool(toolSelection); + Brush * cBrush = gameModel->GetBrush(); + if(!activeTool || !cBrush) + return; + activeTool->Click(sim, cBrush, point); +} + +void GameController::StampRegion(ui::Point point1, ui::Point point2) +{ + GameSave * newSave; + newSave = gameModel->GetSimulation()->Save(point1.X, point1.Y, point2.X, point2.Y); + if(newSave) + { + newSave->paused = gameModel->GetPaused(); + gameModel->AddStamp(newSave); + } + else + new ErrorMessage("Could not create stamp", "Error generating save file"); +} + +void GameController::CopyRegion(ui::Point point1, ui::Point point2) +{ + GameSave * newSave; + newSave = gameModel->GetSimulation()->Save(point1.X, point1.Y, point2.X, point2.Y); + if(newSave) + { + newSave->paused = gameModel->GetPaused(); + gameModel->SetClipboard(newSave); + } +} + +void GameController::CutRegion(ui::Point point1, ui::Point point2) +{ + CopyRegion(point1, point2); + gameModel->GetSimulation()->clear_area(point1.X, point1.Y, point2.X-point1.X, point2.Y-point1.Y); +} + +bool GameController::MouseMove(int x, int y, int dx, int dy) +{ + return commandInterface->OnMouseMove(x, y, dx, dy); +} + +bool GameController::BrushChanged(int brushType, int rx, int ry) +{ + return commandInterface->OnBrushChanged(brushType, rx, ry); +} + +bool GameController::MouseDown(int x, int y, unsigned button) +{ + return commandInterface->OnMouseDown(x, y, button); +} + +bool GameController::MouseUp(int x, int y, unsigned button) +{ + bool ret = commandInterface->OnMouseUp(x, y, button); + ui::Point point = PointTranslate(ui::Point(x, y)); + x = point.X; + y = point.Y; + if(ret && y<YRES && x<XRES) + { + if (gameModel->GetActiveTool(0)->GetIdentifier() != "DEFAULT_UI_SIGN" || button != BUTTON_LEFT) //If it's not a sign tool or you are right/middle clicking + { + Simulation * sim = gameModel->GetSimulation(); + for (std::vector<sign>::iterator iter = sim->signs.begin(), end = sim->signs.end(); iter != end; ++iter) + { + int signx, signy, signw, signh; + (*iter).pos((*iter).getText(sim), signx, signy, signw, signh); + if (x>=signx && x<=signx+signw && y>=signy && y<=signy+signh) + { + if (sregexp((*iter).text.c_str(), "^{[c|t]:[0-9]*|.*}$")==0) + { + const char * signText = (*iter).text.c_str(); + char buff[256]; + int sldr; + + memset(buff, 0, sizeof(buff)); + + for (sldr=3; signText[sldr] != '|'; sldr++) + buff[sldr-3] = signText[sldr]; + + buff[sldr-3] = '\0'; + + int tempSaveID = format::StringToNumber<int>(std::string(buff)); + if (tempSaveID) + { + if ((*iter).text.c_str()[1] == 'c') + OpenSavePreview(tempSaveID, 0); + else if ((*iter).text.c_str()[1] == 't') + { + char url[256]; + sprintf(url, "http://powdertoy.co.uk/Discussions/Thread/View.html?Thread=%i", tempSaveID); + OpenURI(url); + } + } + break; + } + } + } + } + } + return ret; +} + +bool GameController::MouseWheel(int x, int y, int d) +{ + return commandInterface->OnMouseWheel(x, y, d); +} + +bool GameController::KeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + bool ret = commandInterface->OnKeyPress(key, character, shift, ctrl, alt); + if(ret) + { + Simulation * sim = gameModel->GetSimulation(); + if (key == KEY_RIGHT) + { + sim->player.comm = (int)(sim->player.comm)|0x02; //Go right command + } + if (key == KEY_LEFT) + { + sim->player.comm = (int)(sim->player.comm)|0x01; //Go left command + } + if (key == KEY_DOWN && ((int)(sim->player.comm)&0x08)!=0x08) + { + sim->player.comm = (int)(sim->player.comm)|0x08; //Use element command + } + if (key == KEY_UP && ((int)(sim->player.comm)&0x04)!=0x04) + { + sim->player.comm = (int)(sim->player.comm)|0x04; //Jump command + } + + if (key == KEY_d) + { + sim->player2.comm = (int)(sim->player2.comm)|0x02; //Go right command + } + if (key == KEY_a) + { + sim->player2.comm = (int)(sim->player2.comm)|0x01; //Go left command + } + if (key == KEY_s && ((int)(sim->player2.comm)&0x08)!=0x08) + { + sim->player2.comm = (int)(sim->player2.comm)|0x08; //Use element command + } + if (key == KEY_w && ((int)(sim->player2.comm)&0x04)!=0x04) + { + sim->player2.comm = (int)(sim->player2.comm)|0x04; //Jump command + } + + if((!sim->elementCount[PT_STKM2] || ctrl) && gameView->GetSelectMode() == SelectNone) + { + switch(key) + { + case 'w': + SwitchGravity(); + break; + case 'd': + gameView->ToggleDebug(); + break; + case 's': + gameView->BeginStampSelection(); + break; + } + } + } + return ret; +} + +bool GameController::KeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + bool ret = commandInterface->OnKeyRelease(key, character, shift, ctrl, alt); + if(ret) + { + Simulation * sim = gameModel->GetSimulation(); + if (key == KEY_RIGHT || key == KEY_LEFT) + { + sim->player.pcomm = sim->player.comm; //Saving last movement + sim->player.comm = (int)(sim->player.comm)&12; //Stop command + } + if (key == KEY_UP) + { + sim->player.comm = (int)(sim->player.comm)&11; + } + if (key == KEY_DOWN) + { + sim->player.comm = (int)(sim->player.comm)&7; + } + + if (key == KEY_d || key == KEY_a) + { + sim->player2.pcomm = sim->player2.comm; //Saving last movement + sim->player2.comm = (int)(sim->player2.comm)&12; //Stop command + } + if (key == KEY_w) + { + sim->player2.comm = (int)(sim->player2.comm)&11; + } + if (key == KEY_s) + { + sim->player2.comm = (int)(sim->player2.comm)&7; + } + } + return ret; +} + +void GameController::Tick() +{ + if(firstTick) + { + ((LuaScriptInterface*)commandInterface)->Init(); + if(!Client::Ref().GetPrefBool("InstallCheck", false)) + { + Client::Ref().SetPref("InstallCheck", true); + Install(); + } + firstTick = false; + } + for(std::vector<DebugInfo*>::iterator iter = debugInfo.begin(), end = debugInfo.end(); iter != end; iter++) + { + (*iter)->Draw(ui::Point(10, 10)); + } + commandInterface->OnTick(); +} + +void GameController::Exit() +{ + if(ui::Engine::Ref().GetWindow() == gameView) + ui::Engine::Ref().CloseWindow(); + HasDone = true; +} + +void GameController::ResetAir() +{ + Simulation * sim = gameModel->GetSimulation(); + sim->air->Clear(); + for (int i = 0; i < NPART; i++) + { + if (sim->parts[i].type == PT_QRTZ || sim->parts[i].type == PT_GLAS) + { + sim->parts[i].pavg[0] = sim->parts[i].pavg[1] = 0; + } + } +} + +void GameController::ResetSpark() +{ + Simulation * sim = gameModel->GetSimulation(); + for (int i = 0; i < NPART; i++) + if (sim->parts[i].type == PT_SPRK) + { + if (sim->parts[i].ctype >= 0 && sim->parts[i].ctype < PT_NUM && sim->elements[sim->parts[i].ctype].Enabled) + { + sim->parts[i].type = sim->parts[i].ctype; + sim->parts[i].life = 0; + } + else + sim->kill_part(i); + } +} + +void GameController::SwitchGravity() +{ + gameModel->GetSimulation()->gravityMode = (gameModel->GetSimulation()->gravityMode+1)%3; + + switch (gameModel->GetSimulation()->gravityMode) + { + case 0: + gameModel->SetInfoTip("Gravity: Vertical"); + break; + case 1: + gameModel->SetInfoTip("Gravity: Off"); + break; + case 2: + gameModel->SetInfoTip("Gravity: Radial"); + break; + } +} + +void GameController::SwitchAir() +{ + gameModel->GetSimulation()->air->airMode = (gameModel->GetSimulation()->air->airMode+1)%5; + + switch (gameModel->GetSimulation()->air->airMode) + { + case 0: + gameModel->SetInfoTip("Air: On"); + break; + case 1: + gameModel->SetInfoTip("Air: Pressure Off"); + break; + case 2: + gameModel->SetInfoTip("Air: Velocity Off"); + break; + case 3: + gameModel->SetInfoTip("Air: Off"); + break; + case 4: + gameModel->SetInfoTip("Air: No Update"); + break; + } +} + +void GameController::ToggleAHeat() +{ + gameModel->SetAHeatEnable(!gameModel->GetAHeatEnable()); +} + + +void GameController::LoadRenderPreset(int presetNum) +{ + Renderer * renderer = gameModel->GetRenderer(); + RenderPreset preset = renderer->renderModePresets[presetNum]; + gameModel->SetInfoTip(preset.Name); + renderer->SetRenderMode(preset.RenderModes); + renderer->SetDisplayMode(preset.DisplayModes); + renderer->SetColourMode(preset.ColourMode); +} + +void GameController::Update() +{ + ui::Point pos = gameView->GetMousePosition(); + if(pos.X >= 0 && pos.Y >= 0 && pos.X < XRES && pos.Y < YRES) + { + gameModel->GetRenderer()->mousePosX = pos.X; + gameModel->GetRenderer()->mousePosY = pos.Y; + gameView->SetSample(gameModel->GetSimulation()->Get(pos.X, pos.Y)); + } + + gameModel->GetSimulation()->update_particles(); + if(renderOptions && renderOptions->HasExited) + { + delete renderOptions; + renderOptions = NULL; + } + + if(search && search->HasExited) + { + delete search; + search = NULL; + } + + if(activePreview && activePreview->HasExited) + { + delete activePreview; + activePreview = NULL; + } + + if(loginWindow && loginWindow->HasExited) + { + delete loginWindow; + loginWindow = NULL; + } + + if(localBrowser && localBrowser->HasDone) + { + delete localBrowser; + localBrowser = NULL; + } +} + +void GameController::SetZoomEnabled(bool zoomEnabled) +{ + gameModel->SetZoomEnabled(zoomEnabled); +} + +void GameController::SetToolStrength(float value) +{ + gameModel->SetToolStrength(value); +} + +void GameController::SetZoomPosition(ui::Point position) +{ + ui::Point zoomPosition = position-(gameModel->GetZoomSize()/2); + if(zoomPosition.X < 0) + zoomPosition.X = 0; + if(zoomPosition.Y < 0) + zoomPosition.Y = 0; + if(zoomPosition.X >= XRES-gameModel->GetZoomSize()) + zoomPosition.X = XRES-gameModel->GetZoomSize(); + if(zoomPosition.Y >= YRES-gameModel->GetZoomSize()) + zoomPosition.Y = YRES-gameModel->GetZoomSize(); + + ui::Point zoomWindowPosition = ui::Point(0, 0); + if(position.X < XRES/2) + zoomWindowPosition.X = XRES-(gameModel->GetZoomSize()*gameModel->GetZoomFactor()); + + gameModel->SetZoomPosition(zoomPosition); + gameModel->SetZoomWindowPosition(zoomWindowPosition); +} + +void GameController::SetPaused(bool pauseState) +{ + gameModel->SetPaused(pauseState); +} + +void GameController::SetPaused() +{ + gameModel->SetPaused(!gameModel->GetPaused()); +} + +void GameController::SetDecoration(bool decorationState) +{ + gameModel->SetDecoration(decorationState); +} + +void GameController::SetDecoration() +{ + gameModel->SetDecoration(!gameModel->GetDecoration()); +} + +void GameController::ShowGravityGrid() +{ + gameModel->ShowGravityGrid(!gameModel->GetGravityGrid()); + gameModel->UpdateQuickOptions(); +} + +void GameController::SetHudEnable(bool hudState) +{ + gameView->SetHudEnable(hudState); +} + +void GameController::SetActiveColourPreset(int preset) +{ + gameModel->SetActiveColourPreset(preset); +} + +void GameController::SetColour(ui::Colour colour) +{ + gameModel->SetColourSelectorColour(colour); + gameModel->SetPresetColour(colour); +} + +void GameController::SetActiveMenu(Menu * menu) +{ + gameModel->SetActiveMenu(menu); + vector<Menu*> menuList = gameModel->GetMenuList(); + bool set = false; + for(int i = 0; i < menuList.size(); i++) + { + if(menuList[i]==menu && i == SC_DECO) + { + gameModel->SetColourSelectorVisibility(true); + set = true; + } + } + if(!set) + gameModel->SetColourSelectorVisibility(false); +} + +std::vector<Menu*> GameController::GetMenuList() +{ + return gameModel->GetMenuList(); +} + +void GameController::ActiveToolChanged(int toolSelection, Tool *tool) +{ + commandInterface->OnActiveToolChanged(toolSelection, tool); +} + +void GameController::SetActiveTool(int toolSelection, Tool * tool) +{ + gameModel->SetActiveTool(toolSelection, tool); + gameModel->GetRenderer()->gravityZonesEnabled = false; + gameModel->SetLastTool(tool); + for(int i = 0; i < 3; i++) + { + if(gameModel->GetActiveTool(i) == gameModel->GetMenuList().at(SC_WALL)->GetToolList().at(WL_GRAV)) + { + gameModel->GetRenderer()->gravityZonesEnabled = true; + } + } +} + +void GameController::OpenSearch() +{ + if(!search) + search = new SearchController(new SearchCallback(this)); + ui::Engine::Ref().ShowWindow(search->GetView()); +} + +void GameController::OpenLocalSaveWindow(bool asCurrent) +{ + Simulation * sim = gameModel->GetSimulation(); + GameSave * gameSave = sim->Save(); + gameSave->paused = gameModel->GetPaused(); + gameSave->gravityMode = sim->gravityMode; + gameSave->airMode = sim->air->airMode; + gameSave->legacyEnable = sim->legacy_enable; + gameSave->waterEEnabled = sim->water_equal_test; + gameSave->gravityEnable = sim->grav->ngrav_enable; + if(!gameSave) + { + new ErrorMessage("Error", "Unable to build save."); + } + else + { + std::string filename = ""; + if (gameModel->GetSaveFile()) + filename = gameModel->GetSaveFile()->GetDisplayName(); + SaveFile tempSave(filename); + tempSave.SetGameSave(gameSave); + + if (!asCurrent || !gameModel->GetSaveFile()) + { + class LocalSaveCallback: public FileSavedCallback + { + GameController * c; + public: + LocalSaveCallback(GameController * _c): c(_c) {} + virtual ~LocalSaveCallback() {}; + virtual void FileSaved(SaveFile* file) + { + c->gameModel->SetSaveFile(file); + } + }; + + new LocalSaveActivity(tempSave, new LocalSaveCallback(this)); + } + else if (gameModel->GetSaveFile()) + { + Client::Ref().MakeDirectory(LOCAL_SAVE_DIR); + Client::Ref().WriteFile(gameSave->Serialise(), gameModel->GetSaveFile()->GetName()); + } + } +} + +void GameController::LoadSaveFile(SaveFile * file) +{ + gameModel->SetSaveFile(file); +} + + +void GameController::LoadSave(SaveInfo * save) +{ + gameModel->SetSave(save); +} + +void GameController::OpenSavePreview(int saveID, int saveDate) +{ + activePreview = new PreviewController(saveID, new SaveOpenCallback(this)); + ui::Engine::Ref().ShowWindow(activePreview->GetView()); +} + +void GameController::OpenSavePreview() +{ + if(gameModel->GetSave()) + { + activePreview = new PreviewController(gameModel->GetSave()->GetID(), new SaveOpenCallback(this)); + ui::Engine::Ref().ShowWindow(activePreview->GetView()); + } +} + +void GameController::OpenLocalBrowse() +{ + class LocalSaveOpenCallback: public FileSelectedCallback + { + GameController * c; + public: + LocalSaveOpenCallback(GameController * _c): c(_c) {} + virtual ~LocalSaveOpenCallback() {}; + virtual void FileSelected(SaveFile* file) + { + c->LoadSaveFile(file); + delete file; + } + }; + new FileBrowserActivity(LOCAL_SAVE_DIR PATH_SEP, new LocalSaveOpenCallback(this)); +} + +void GameController::OpenLogin() +{ + if(Client::Ref().GetAuthUser().ID) + { + new ProfileActivity(Client::Ref().GetAuthUser().Username); + } + else + { + loginWindow = new LoginController(); + ui::Engine::Ref().ShowWindow(loginWindow->GetView()); + } +} + +void GameController::OpenElementSearch() +{ + vector<Tool*> toolList; + vector<Menu*> menuList = gameModel->GetMenuList(); + for(std::vector<Menu*>::iterator iter = menuList.begin(), end = menuList.end(); iter!=end; ++iter) { + if(!(*iter)) + continue; + vector<Tool*> menuToolList = (*iter)->GetToolList(); + if(!menuToolList.size()) + continue; + toolList.insert(toolList.end(), menuToolList.begin(), menuToolList.end()); + } + vector<Tool*> hiddenTools = gameModel->GetUnlistedTools(); + toolList.insert(toolList.end(), hiddenTools.begin(), hiddenTools.end()); + new ElementSearchActivity(gameModel, toolList); +} + +void GameController::OpenColourPicker() +{ + class ColourPickerCallback: public ColourPickedCallback + { + GameController * c; + public: + ColourPickerCallback(GameController * _c): c(_c) {} + virtual ~ColourPickerCallback() {}; + virtual void ColourPicked(ui::Colour colour) + { + c->SetColour(colour); + } + }; + new ColourPickerActivity(gameModel->GetColourSelectorColour(), new ColourPickerCallback(this)); +} + +void GameController::OpenTags() +{ + if(gameModel->GetUser().ID) + { + if(gameModel->GetSave() && gameModel->GetSave()->GetID()) + { + tagsWindow = new TagsController(new TagsCallback(this), gameModel->GetSave()); + ui::Engine::Ref().ShowWindow(tagsWindow->GetView()); + } + else + { + new ErrorMessage("Error", "No save open"); + } + } + else + { + new ErrorMessage("Error", "You need to login to edit tags."); + } +} + +void GameController::OpenStamps() +{ + localBrowser = new LocalBrowserController(new StampsCallback(this)); + ui::Engine::Ref().ShowWindow(localBrowser->GetView()); +} + +void GameController::OpenOptions() +{ + options = new OptionsController(gameModel, new OptionsCallback(this)); + ui::Engine::Ref().ShowWindow(options->GetView()); + +} + +void GameController::ShowConsole() +{ + if(!console) + console = new ConsoleController(NULL, commandInterface); + if (console->GetView() != ui::Engine::Ref().GetWindow()) + ui::Engine::Ref().ShowWindow(console->GetView()); +} + +void GameController::HideConsole() +{ + if(!console) + return; + if (console->GetView() == ui::Engine::Ref().GetWindow()) + ui::Engine::Ref().CloseWindow(); +} + +void GameController::OpenRenderOptions() +{ + renderOptions = new RenderController(gameModel->GetRenderer(), new RenderCallback(this)); + ui::Engine::Ref().ShowWindow(renderOptions->GetView()); +} + +void GameController::OpenSaveWindow() +{ + class SaveUploadedCallback: public ServerSaveActivity::SaveUploadedCallback + { + GameController * c; + public: + SaveUploadedCallback(GameController * _c): c(_c) {} + virtual ~SaveUploadedCallback() {}; + virtual void SaveUploaded(SaveInfo save) + { + c->LoadSave(&save); + } + }; + if(gameModel->GetUser().ID) + { + Simulation * sim = gameModel->GetSimulation(); + GameSave * gameSave = sim->Save(); + gameSave->paused = gameModel->GetPaused(); + gameSave->gravityMode = sim->gravityMode; + gameSave->airMode = sim->air->airMode; + gameSave->legacyEnable = sim->legacy_enable; + gameSave->waterEEnabled = sim->water_equal_test; + gameSave->gravityEnable = sim->grav->ngrav_enable; + if(!gameSave) + { + new ErrorMessage("Error", "Unable to build save."); + } + else + { + if(gameModel->GetSave()) + { + SaveInfo tempSave(*gameModel->GetSave()); + tempSave.SetGameSave(gameSave); + new ServerSaveActivity(tempSave, new SaveUploadedCallback(this)); + } + else + { + SaveInfo tempSave(0, 0, 0, 0, gameModel->GetUser().Username, ""); + tempSave.SetGameSave(gameSave); + new ServerSaveActivity(tempSave, new SaveUploadedCallback(this)); + } + } + } + else + { + new ErrorMessage("Error", "You need to login to upload saves."); + } +} + +void GameController::SaveAsCurrent() +{ + + class SaveUploadedCallback: public ServerSaveActivity::SaveUploadedCallback + { + GameController * c; + public: + SaveUploadedCallback(GameController * _c): c(_c) {} + virtual ~SaveUploadedCallback() {}; + virtual void SaveUploaded(SaveInfo save) + { + c->LoadSave(&save); + } + }; + + if(gameModel->GetSave() && gameModel->GetUser().ID && gameModel->GetUser().Username == gameModel->GetSave()->GetUserName()) + { + Simulation * sim = gameModel->GetSimulation(); + GameSave * gameSave = sim->Save(); + gameSave->paused = gameModel->GetPaused(); + gameSave->gravityMode = sim->gravityMode; + gameSave->airMode = sim->air->airMode; + gameSave->legacyEnable = sim->legacy_enable; + gameSave->waterEEnabled = sim->water_equal_test; + gameSave->gravityEnable = sim->grav->ngrav_enable; + if(!gameSave) + { + new ErrorMessage("Error", "Unable to build save."); + } + else + { + if(gameModel->GetSave()) + { + SaveInfo tempSave(*gameModel->GetSave()); + tempSave.SetGameSave(gameSave); + new ServerSaveActivity(tempSave, true, new SaveUploadedCallback(this)); + } + else + { + SaveInfo tempSave(0, 0, 0, 0, gameModel->GetUser().Username, ""); + tempSave.SetGameSave(gameSave); + new ServerSaveActivity(tempSave, true, new SaveUploadedCallback(this)); + } + } + } + else if(gameModel->GetUser().ID) + { + OpenSaveWindow(); + } + else + { + new ErrorMessage("Error", "You need to login to upload saves."); + } +} + +void GameController::FrameStep() +{ + gameModel->FrameStep(1); + gameModel->SetPaused(true); +} + +void GameController::Vote(int direction) +{ + if(gameModel->GetSave() && gameModel->GetUser().ID && gameModel->GetSave()->GetID() && gameModel->GetSave()->GetVote()==0) + { + try + { + gameModel->SetVote(direction); + } + catch(GameModelException & ex) + { + new ErrorMessage("Error while voting", ex.what()); + } + } +} + +void GameController::ChangeBrush() +{ + gameModel->SetBrush(gameModel->GetBrushID()+1); + BrushChanged(gameModel->GetBrushID(), gameModel->GetBrush()->GetRadius().X, gameModel->GetBrush()->GetRadius().Y); +} + +void GameController::ClearSim() +{ + gameModel->SetSave(NULL); + gameModel->ClearSimulation(); +} + +void GameController::ReloadSim() +{ + if(gameModel->GetSave() && gameModel->GetSave()->GetGameSave()) + { + gameModel->SetSave(gameModel->GetSave()); + } + else if(gameModel->GetSaveFile() && gameModel->GetSaveFile()->GetGameSave()) + { + gameModel->SetSaveFile(gameModel->GetSaveFile()); + } +} + +std::string GameController::ElementResolve(int type) +{ + if(gameModel && gameModel->GetSimulation() && gameModel->GetSimulation()->elements && type >= 0 && type < PT_NUM) + return std::string(gameModel->GetSimulation()->elements[type].Name); + else + return ""; +} + +std::string GameController::WallName(int type) +{ + if(gameModel && gameModel->GetSimulation() && gameModel->GetSimulation()->wtypes && type >= 0 && type < UI_WALLCOUNT) + return std::string(gameModel->GetSimulation()->wtypes[type].name); + else + return ""; +} + +void GameController::NotifyAuthUserChanged(Client * sender) +{ + User newUser = sender->GetAuthUser(); + gameModel->SetUser(newUser); +} + +void GameController::NotifyNewNotification(Client * sender, std::pair<std::string, std::string> notification) +{ + class LinkNotification : public Notification + { + std::string link; + public: + LinkNotification(std::string link_, std::string message) : link(link_), Notification(message) {} + virtual ~LinkNotification() {} + + virtual void Action() + { + OpenURI(link); + } + }; + gameModel->AddNotification(new LinkNotification(notification.second, notification.first)); +} + +void GameController::NotifyUpdateAvailable(Client * sender) +{ + class UpdateConfirmation: public ConfirmDialogueCallback { + public: + GameController * c; + UpdateConfirmation(GameController * c_) { c = c_; } + virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) { + if (result == ConfirmPrompt::ResultOkay) + { + c->RunUpdater(); + } + } + virtual ~UpdateConfirmation() { } + }; + + class UpdateNotification : public Notification + { + GameController * c; + public: + UpdateNotification(GameController * c, std::string message) : c(c), Notification(message) {} + virtual ~UpdateNotification() {} + + virtual void Action() + { + std::string currentVersion, newVersion; +#ifdef BETA + currentVersion = MTOS(SAVE_VERSION) "." MTOS(MINOR_VERSION) " Beta, Build " MTOS(BUILD_NUM); +#elif defined(SNAPSHOT) + currentVersion = "Snapshot " MTOS(SNAPSHOT_ID); +#else + currentVersion = MTOS(SAVE_VERSION) "." MTOS(MINOR_VERSION) " Stable, Build " MTOS(BUILD_NUM); +#endif + + UpdateInfo info = Client::Ref().GetUpdateInfo(); + if(info.Type == UpdateInfo::Beta) + newVersion = format::NumberToString<int>(info.Major) + " " + format::NumberToString<int>(info.Minor) + " Beta, Build " + format::NumberToString<int>(info.Build); + else if(info.Type == UpdateInfo::Snapshot) + newVersion = "Snapshot " + format::NumberToString<int>(info.Time); + else if(info.Type == UpdateInfo::Stable) + newVersion = format::NumberToString<int>(info.Major) + " " + format::NumberToString<int>(info.Minor) + " Stable, Build " + format::NumberToString<int>(info.Build); + + new ConfirmPrompt("Run Updater", "Are you sure you want to run the updater, please save any changes before updating.\n\nCurrent version:\n " + currentVersion + "\nNew version:\n " + newVersion, new UpdateConfirmation(c)); + } + }; + + switch(sender->GetUpdateInfo().Type) + { + case UpdateInfo::Snapshot: + gameModel->AddNotification(new UpdateNotification(this, std::string("A new snapshot is available - click here to update"))); + break; + case UpdateInfo::Stable: + gameModel->AddNotification(new UpdateNotification(this, std::string("A new version is available - click here to update"))); + break; + case UpdateInfo::Beta: + gameModel->AddNotification(new UpdateNotification(this, std::string("A new beta is available - click here to update"))); + break; + } +} + +void GameController::RemoveNotification(Notification * notification) +{ + gameModel->RemoveNotification(notification); +} + +void GameController::RunUpdater() +{ + Exit(); + new UpdateActivity(); +} diff --git a/src/gui/game/GameController.h b/src/gui/game/GameController.h new file mode 100644 index 0000000..644d25c --- /dev/null +++ b/src/gui/game/GameController.h @@ -0,0 +1,155 @@ + #ifndef GAMECONTROLLER_H +#define GAMECONTROLLER_H + +#include <queue> +#include "GameView.h" +#include "GameModel.h" +#include "simulation/Simulation.h" +#include "gui/interface/Point.h" +#include "gui/search/SearchController.h" +#include "gui/render/RenderController.h" +#include "gui/preview/PreviewController.h" +#include "gui/login/LoginController.h" +#include "gui/tags/TagsController.h" +#include "gui/console/ConsoleController.h" +#include "gui/localbrowser/LocalBrowserController.h" +#include "gui/options/OptionsController.h" +//#include "cat/TPTScriptInterface.h" +#include "cat/LuaScriptInterface.h" +#include "client/ClientListener.h" +#include "RenderPreset.h" +#include "Menu.h" + +using namespace std; + +class DebugInfo; +class Notification; +class GameModel; +class GameView; +class CommandInterface; +class ConsoleController; +class GameController: public ClientListener +{ +private: + //Simulation * sim; + bool firstTick; + int screenshotIndex; + PreviewController * activePreview; + GameView * gameView; + GameModel * gameModel; + SearchController * search; + RenderController * renderOptions; + LoginController * loginWindow; + ConsoleController * console; + TagsController * tagsWindow; + LocalBrowserController * localBrowser; + OptionsController * options; + CommandInterface * commandInterface; + vector<DebugInfo*> debugInfo; +public: + bool HasDone; + class SearchCallback; + class RenderCallback; + class SSaveCallback; + class TagsCallback; + class StampsCallback; + class OptionsCallback; + class SaveOpenCallback; + friend class SaveOpenCallback; + GameController(); + ~GameController(); + GameView * GetView(); + + bool BrushChanged(int brushType, int rx, int ry); + bool MouseMove(int x, int y, int dx, int dy); + bool MouseDown(int x, int y, unsigned button); + bool MouseUp(int x, int y, unsigned button); + bool MouseWheel(int x, int y, int d); + bool KeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + bool KeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt); + void Tick(); + void Exit(); + + void Install(); + + void HistoryRestore(); + void HistorySnapshot(); + + void AdjustGridSize(int direction); + void InvertAirSim(); + void LoadRenderPreset(int presetNum); + void SetZoomEnabled(bool zoomEnable); + void SetZoomPosition(ui::Point position); + void AdjustBrushSize(int direction, bool logarithmic = false, bool xAxis = false, bool yAxis = false); + void AdjustZoomSize(int direction, bool logarithmic = false); + void ToolClick(int toolSelection, ui::Point point); + void DrawPoints(int toolSelection, queue<ui::Point> & pointQueue); + void DrawRect(int toolSelection, ui::Point point1, ui::Point point2); + void DrawLine(int toolSelection, ui::Point point1, ui::Point point2); + void DrawFill(int toolSelection, ui::Point point); + void StampRegion(ui::Point point1, ui::Point point2); + void CopyRegion(ui::Point point1, ui::Point point2); + void CutRegion(ui::Point point1, ui::Point point2); + void Update(); + void SetPaused(bool pauseState); + void SetPaused(); + void SetDecoration(bool decorationState); + void SetDecoration(); + void ShowGravityGrid(); + void SetHudEnable(bool hudState); + void SetActiveMenu(Menu * menu); + std::vector<Menu*> GetMenuList(); + void SetActiveTool(int toolSelection, Tool * tool); + void ActiveToolChanged(int toolSelection, Tool *tool); + void SetActiveColourPreset(int preset); + void SetColour(ui::Colour colour); + void SetToolStrength(float value); + void LoadSaveFile(SaveFile * file); + void LoadSave(SaveInfo * save); + void OpenSearch(); + void OpenLogin(); + void OpenTags(); + void OpenSavePreview(int saveID, int saveDate); + void OpenSavePreview(); + void OpenLocalSaveWindow(bool asCurrent); + void OpenLocalBrowse(); + void OpenOptions(); + void OpenRenderOptions(); + void OpenSaveWindow(); + void SaveAsCurrent(); + void OpenStamps(); + void OpenElementSearch(); + void OpenColourPicker(); + void PlaceSave(ui::Point position); + void ClearSim(); + void ReloadSim(); + void Vote(int direction); + void ChangeBrush(); + void ShowConsole(); + void HideConsole(); + void FrameStep(); + void TranslateSave(ui::Point point); + void TransformSave(matrix2d transform); + ui::Point PointTranslate(ui::Point point); + ui::Point NormaliseBlockCoord(ui::Point point); + std::string ElementResolve(int type); + std::string WallName(int type); + + void ResetAir(); + void ResetSpark(); + void SwitchGravity(); + void SwitchAir(); + void ToggleAHeat(); + + void LoadClipboard(); + void LoadStamp(); + + void RemoveNotification(Notification * notification); + + virtual void NotifyUpdateAvailable(Client * sender); + virtual void NotifyAuthUserChanged(Client * sender); + virtual void NotifyNewNotification(Client * sender, std::pair<std::string, std::string> notification); + void RunUpdater(); +}; + +#endif // GAMECONTROLLER_H diff --git a/src/gui/game/GameModel.cpp b/src/gui/game/GameModel.cpp new file mode 100644 index 0000000..37194a5 --- /dev/null +++ b/src/gui/game/GameModel.cpp @@ -0,0 +1,1151 @@ +#include "gui/interface/Engine.h" +#include "GameModel.h" +#include "GameView.h" +#include "simulation/Simulation.h" +#include "simulation/Air.h" +#include "simulation/Tools.h" +#include "graphics/Renderer.h" +#include "gui/interface/Point.h" +#include "Brush.h" +#include "EllipseBrush.h" +#include "TriangleBrush.h" +#include "BitmapBrush.h" +#include "client/Client.h" +#include "client/GameSave.h" +#include "gui/game/DecorationTool.h" +#include "GameModelException.h" +#include "QuickOptions.h" +#include "Format.h" + +GameModel::GameModel(): + sim(NULL), + ren(NULL), + currentBrush(0), + currentUser(0, ""), + currentSave(NULL), + currentFile(NULL), + colourSelector(false), + clipboard(NULL), + stamp(NULL), + placeSave(NULL), + colour(255, 0, 0, 255), + toolStrength(1.0f), + activeColourPreset(-1), + activeMenu(NULL), + edgeMode(0) +{ + sim = new Simulation(); + ren = new Renderer(ui::Engine::Ref().g, sim); + + activeTools = regularToolset; + + std::fill(decoToolset, decoToolset+3, (Tool*)NULL); + std::fill(regularToolset, regularToolset+3, (Tool*)NULL); + + //Default render prefs + std::vector<unsigned int> tempArray; + tempArray.push_back(RENDER_FIRE); + tempArray.push_back(RENDER_EFFE); + tempArray.push_back(RENDER_BASC); + ren->SetRenderMode(tempArray); + tempArray.clear(); + + ren->SetDisplayMode(tempArray); + + ren->SetColourMode(0); + + //Load config into renderer + try + { + ren->SetColourMode(Client::Ref().GetPrefUInteger("Renderer.ColourMode", 0)); + + vector<unsigned int> tempArray = Client::Ref().GetPrefUIntegerArray("Renderer.DisplayModes"); + if(tempArray.size()) + { + std::vector<unsigned int> displayModes(tempArray.begin(), tempArray.end()); + ren->SetDisplayMode(displayModes); + } + + tempArray = Client::Ref().GetPrefUIntegerArray("Renderer.RenderModes"); + if(tempArray.size()) + { + std::vector<unsigned int> renderModes(tempArray.begin(), tempArray.end()); + ren->SetRenderMode(renderModes); + } + + ren->gravityFieldEnabled = Client::Ref().GetPrefBool("Renderer.GravityField", false); + ren->decorations_enable = Client::Ref().GetPrefBool("Renderer.Decorations", true); + } + catch(json::Exception & e) + { + } + + //Load config into simulation + edgeMode = Client::Ref().GetPrefInteger("Simulation.EdgeMode", 0); + sim->SetEdgeMode(edgeMode); + + //Load last user + if(Client::Ref().GetAuthUser().ID) + { + currentUser = Client::Ref().GetAuthUser(); + } + + //Set stamp to first stamp in list + vector<string> stamps = Client::Ref().GetStamps(0, 1); + if(stamps.size()>0) + { + SaveFile * stampFile = Client::Ref().GetStamp(stamps[0]); + if(stampFile && stampFile->GetGameSave()) + stamp = stampFile->GetGameSave(); + } + + BuildMenus(); + + //Set default brush palette + brushList.push_back(new EllipseBrush(ui::Point(4, 4))); + brushList.push_back(new Brush(ui::Point(4, 4))); + brushList.push_back(new TriangleBrush(ui::Point(4, 4))); + + //Load more from brushes folder + std::vector<string> brushFiles = Client::Ref().DirectorySearch(BRUSH_DIR, "", ".ptb"); + for(int i = 0; i < brushFiles.size(); i++) + { + std::vector<unsigned char> brushData = Client::Ref().ReadFile(brushFiles[i]); + if(!brushData.size()) + { + std::cout << "Brushes: Skipping " << brushFiles[i] << ". Could not open" << std::endl; + continue; + } + int dimension = std::sqrt((float)brushData.size()); + if(dimension * dimension != brushData.size()) + { + std::cout << "Brushes: Skipping " << brushFiles[i] << ". Invalid bitmap size" << std::endl; + continue; + } + brushList.push_back(new BitmapBrush(brushData, ui::Point(dimension, dimension))); + } + + //Set default decoration colour + unsigned char colourR = min(Client::Ref().GetPrefInteger("Decoration.Red", 200), 255); + unsigned char colourG = min(Client::Ref().GetPrefInteger("Decoration.Green", 100), 255); + unsigned char colourB = min(Client::Ref().GetPrefInteger("Decoration.Blue", 50), 255); + unsigned char colourA = min(Client::Ref().GetPrefInteger("Decoration.Alpha", 255), 255); + + SetColourSelectorColour(ui::Colour(colourR, colourG, colourB, colourA)); + + colourPresets.push_back(ui::Colour(255, 255, 255)); + colourPresets.push_back(ui::Colour(0, 255, 255)); + colourPresets.push_back(ui::Colour(255, 0, 255)); + colourPresets.push_back(ui::Colour(255, 255, 0)); + colourPresets.push_back(ui::Colour(255, 0, 0)); + colourPresets.push_back(ui::Colour(0, 255, 0)); + colourPresets.push_back(ui::Colour(0, 0, 255)); +} + +GameModel::~GameModel() +{ + //Save to config: + Client::Ref().SetPref("Renderer.ColourMode", ren->GetColourMode()); + + std::vector<unsigned int> displayModes = ren->GetDisplayMode(); + Client::Ref().SetPref("Renderer.DisplayModes", std::vector<unsigned int>(displayModes.begin(), displayModes.end())); + + std::vector<unsigned int> renderModes = ren->GetRenderMode(); + Client::Ref().SetPref("Renderer.RenderModes", std::vector<unsigned int>(renderModes.begin(), renderModes.end())); + + Client::Ref().SetPref("Renderer.GravityField", (bool)ren->gravityFieldEnabled); + Client::Ref().SetPref("Renderer.Decorations", (bool)ren->decorations_enable); + + Client::Ref().SetPref("Simulation.EdgeMode", sim->edgeMode); + + Client::Ref().SetPref("Decoration.Red", (int)colour.Red); + Client::Ref().SetPref("Decoration.Green", (int)colour.Green); + Client::Ref().SetPref("Decoration.Blue", (int)colour.Blue); + Client::Ref().SetPref("Decoration.Alpha", (int)colour.Alpha); + + for(int i = 0; i < menuList.size(); i++) + { + delete menuList[i]; + } + for(std::vector<Tool*>::iterator iter = extraElementTools.begin(), end = extraElementTools.end(); iter != end; ++iter) + { + delete *iter; + } + for(int i = 0; i < brushList.size(); i++) + { + delete brushList[i]; + } + delete sim; + delete ren; + if(placeSave) + delete placeSave; + if(clipboard) + delete clipboard; + if(stamp) + delete stamp; + if(currentSave) + delete currentSave; + if(currentFile) + delete currentFile; + //if(activeTools) + // delete[] activeTools; +} + +void GameModel::UpdateQuickOptions() +{ + for(std::vector<QuickOption*>::iterator iter = quickOptions.begin(), end = quickOptions.end(); iter != end; ++iter) + { + QuickOption * option = *iter; + option->Update(); + } +} + +void GameModel::BuildQuickOptionMenu(GameController * controller) +{ + for(std::vector<QuickOption*>::iterator iter = quickOptions.begin(), end = quickOptions.end(); iter != end; ++iter) + { + delete *iter; + } + quickOptions.clear(); + + quickOptions.push_back(new SandEffectOption(this)); + quickOptions.push_back(new DrawGravOption(this)); + quickOptions.push_back(new DecorationsOption(this)); + quickOptions.push_back(new NGravityOption(this)); + quickOptions.push_back(new AHeatOption(this)); + quickOptions.push_back(new ConsoleShowOption(this, controller)); + + notifyQuickOptionsChanged(); + UpdateQuickOptions(); +} + +void GameModel::BuildMenus() +{ + char lastMenu = 0; + if(activeMenu) + lastMenu = activeMenu->GetIcon(); + + std::string activeToolIdentifiers[3]; + if(regularToolset[0]) + activeToolIdentifiers[0] = regularToolset[0]->GetIdentifier(); + if(regularToolset[1]) + activeToolIdentifiers[1] = regularToolset[1]->GetIdentifier(); + if(regularToolset[2]) + activeToolIdentifiers[2] = regularToolset[2]->GetIdentifier(); + + //Empty current menus + for(std::vector<Menu*>::iterator iter = menuList.begin(), end = menuList.end(); iter != end; ++iter) + { + delete *iter; + } + menuList.clear(); + toolList.clear(); + + for(std::vector<Tool*>::iterator iter = extraElementTools.begin(), end = extraElementTools.end(); iter != end; ++iter) + { + delete *iter; + } + extraElementTools.clear(); + elementTools.clear(); + + //Create menus + for(int i = 0; i < SC_TOTAL; i++) + { + menuList.push_back(new Menu((const char)sim->msections[i].icon[0], sim->msections[i].name)); + } + + //Build menus from Simulation elements + for(int i = 0; i < PT_NUM; i++) + { + if(sim->elements[i].Enabled) + { + Tool * tempTool; + if(i == PT_LIGH) + { + tempTool = new Element_LIGH_Tool(i, sim->elements[i].Name, sim->elements[i].Description, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), sim->elements[i].Identifier, sim->elements[i].IconGenerator); + } + else if(i == PT_TESC) + { + tempTool = new Element_TESC_Tool(i, sim->elements[i].Name, sim->elements[i].Description, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), sim->elements[i].Identifier, sim->elements[i].IconGenerator); + } + else if(i == PT_STKM || i == PT_FIGH || i == PT_STKM2) + { + tempTool = new PlopTool(i, sim->elements[i].Name, sim->elements[i].Description, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), sim->elements[i].Identifier, sim->elements[i].IconGenerator); + } + else + { + tempTool = new ElementTool(i, sim->elements[i].Name, sim->elements[i].Description, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), sim->elements[i].Identifier, sim->elements[i].IconGenerator); + } + + if(sim->elements[i].MenuSection < SC_TOTAL && sim->elements[i].MenuVisible) + { + menuList[sim->elements[i].MenuSection]->AddTool(tempTool); + } + else + { + extraElementTools.push_back(tempTool); + } + elementTools.push_back(tempTool); + } + } + + //Build menu for GOL types + for(int i = 0; i < NGOL; i++) + { + Tool * tempTool = new GolTool(i, sim->gmenu[i].name, std::string(sim->gmenu[i].description), PIXR(sim->gmenu[i].colour), PIXG(sim->gmenu[i].colour), PIXB(sim->gmenu[i].colour), "DEFAULT_PT_LIFE_"+std::string(sim->gmenu[i].name)); + menuList[SC_LIFE]->AddTool(tempTool); + } + + //Build other menus from wall data + for(int i = 0; i < UI_WALLCOUNT; i++) + { + Tool * tempTool = new WallTool(i, "", std::string(sim->wtypes[i].descs), PIXR(sim->wtypes[i].colour), PIXG(sim->wtypes[i].colour), PIXB(sim->wtypes[i].colour), "DEFAULT_WL_"+format::NumberToString<int>(i), sim->wtypes[i].textureGen); + menuList[SC_WALL]->AddTool(tempTool); + //sim->wtypes[i] + } + + //Add special sign and prop tools + menuList[SC_TOOL]->AddTool(new SampleTool(this)); + menuList[SC_TOOL]->AddTool(new SignTool()); + menuList[SC_TOOL]->AddTool(new PropertyTool()); + menuList[SC_TOOL]->AddTool(new WindTool(0, "WIND", "Create air movement", 64, 64, 64, "DEFAULT_UI_WIND")); + + //Build menu for simtools + for(int i = 0; i < sim->tools.size(); i++) + { + Tool * tempTool; + tempTool = new Tool(i, sim->tools[i]->Name, sim->tools[i]->Description, PIXR(sim->tools[i]->Colour), PIXG(sim->tools[i]->Colour), PIXB(sim->tools[i]->Colour), sim->tools[i]->Identifier); + menuList[SC_TOOL]->AddTool(tempTool); + } + + //Add decoration tools to menu + menuList[SC_DECO]->AddTool(new DecorationTool(DecorationTool::BlendAdd, "ADD", "Colour blending: Add", 0, 0, 0, "DEFAULT_DECOR_ADD")); + menuList[SC_DECO]->AddTool(new DecorationTool(DecorationTool::BlendRemove, "SUB", "Colour blending: Subtract", 0, 0, 0, "DEFAULT_DECOR_SUB")); + menuList[SC_DECO]->AddTool(new DecorationTool(DecorationTool::BlendMultiply, "MUL", "Colour blending: Multiply", 0, 0, 0, "DEFAULT_DECOR_MUL")); + menuList[SC_DECO]->AddTool(new DecorationTool(DecorationTool::BlendDivide, "DIV", "Colour blending: Divide" , 0, 0, 0, "DEFAULT_DECOR_DIV")); + menuList[SC_DECO]->AddTool(new DecorationTool(DecorationTool::BlendSmudge, "SMDG", "Smudge colour", 0, 0, 0, "DEFAULT_DECOR_SMDG")); + menuList[SC_DECO]->AddTool(new DecorationTool(DecorationTool::BlendSet, "SET", "Set colour (No blending)", 0, 0, 0, "DEFAULT_DECOR_SET")); + menuList[SC_DECO]->AddTool(new DecorationTool(DecorationTool::Remove, "CLR", "Clear any set decoration", 0, 0, 0, "DEFAULT_DECOR_CLR")); + decoToolset[0] = GetToolFromIdentifier("DEFAULT_DECOR_SET"); + decoToolset[1] = GetToolFromIdentifier("DEFAULT_DECOR_CLR"); + decoToolset[2] = GetToolFromIdentifier("DEFAULT_UI_SAMPLE"); + + //Set default tools + regularToolset[0] = GetToolFromIdentifier("DEFAULT_PT_DUST"); + regularToolset[1] = GetToolFromIdentifier("DEFAULT_PT_NONE"); + regularToolset[2] = GetToolFromIdentifier("DEFAULT_UI_SAMPLE"); + + + if(activeToolIdentifiers[0].length()) + regularToolset[0] = GetToolFromIdentifier(activeToolIdentifiers[0]); + if(activeToolIdentifiers[1].length()) + regularToolset[1] = GetToolFromIdentifier(activeToolIdentifiers[1]); + if(activeToolIdentifiers[2].length()) + regularToolset[2] = GetToolFromIdentifier(activeToolIdentifiers[2]); + + lastTool = activeTools[0]; + + //Set default menu + activeMenu = menuList[SC_POWDERS]; + + if(lastMenu) + { + for(std::vector<Menu*>::iterator iter = menuList.begin(), end = menuList.end(); iter != end; ++iter) + { + if((*iter)->GetIcon() == lastMenu) + activeMenu = *iter; + } + } + + if(activeMenu) + toolList = activeMenu->GetToolList(); + else + toolList = std::vector<Tool*>(); + + notifyMenuListChanged(); + notifyToolListChanged(); + notifyActiveToolsChanged(); + notifyLastToolChanged(); +} + +Tool * GameModel::GetToolFromIdentifier(std::string identifier) +{ + for(std::vector<Menu*>::iterator iter = menuList.begin(), end = menuList.end(); iter != end; ++iter) + { + std::vector<Tool*> menuTools = (*iter)->GetToolList(); + for(std::vector<Tool*>::iterator titer = menuTools.begin(), tend = menuTools.end(); titer != tend; ++titer) + { + if(identifier == (*titer)->GetIdentifier()) + return *titer; + } + } + return NULL; +} + +void GameModel::SetEdgeMode(int edgeMode) +{ + this->edgeMode = edgeMode; + sim->SetEdgeMode(edgeMode); +} + +int GameModel::GetEdgeMode() +{ + return this->edgeMode; +} + +std::deque<Snapshot*> GameModel::GetHistory() +{ + return history; +} +void GameModel::SetHistory(std::deque<Snapshot*> newHistory) +{ + history = newHistory; +} + +void GameModel::SetVote(int direction) +{ + if(currentSave) + { + RequestStatus status = Client::Ref().ExecVote(currentSave->GetID(), direction); + if(status == RequestOkay) + { + currentSave->vote = direction; + notifySaveChanged(); + } + else + { + throw GameModelException("Could not vote: "+Client::Ref().GetLastError()); + } + } +} + +Brush * GameModel::GetBrush() +{ + return brushList[currentBrush]; +} + +int GameModel::GetBrushID() +{ + return currentBrush; +} + +void GameModel::SetBrush(int i) +{ + currentBrush = i%brushList.size(); + notifyBrushChanged(); +} + +void GameModel::AddObserver(GameView * observer){ + observers.push_back(observer); + + observer->NotifySimulationChanged(this); + observer->NotifyRendererChanged(this); + observer->NotifyPausedChanged(this); + observer->NotifySaveChanged(this); + observer->NotifyBrushChanged(this); + observer->NotifyMenuListChanged(this); + observer->NotifyToolListChanged(this); + observer->NotifyUserChanged(this); + observer->NotifyZoomChanged(this); + observer->NotifyColourSelectorVisibilityChanged(this); + observer->NotifyColourSelectorColourChanged(this); + observer->NotifyColourPresetsChanged(this); + observer->NotifyColourActivePresetChanged(this); + observer->NotifyQuickOptionsChanged(this); + observer->NotifyLastToolChanged(this); + UpdateQuickOptions(); +} + +void GameModel::SetToolStrength(float value) +{ + toolStrength = value; +} + +float GameModel::GetToolStrength() +{ + return toolStrength; +} + +void GameModel::SetActiveMenu(Menu * menu) +{ + for(int i = 0; i < menuList.size(); i++) + { + if(menuList[i]==menu) + { + activeMenu = menu; + toolList = menu->GetToolList(); + notifyToolListChanged(); + + if(menu == menuList[SC_DECO]) + { + if(activeTools != decoToolset) + { + activeTools = decoToolset; + notifyActiveToolsChanged(); + } + } + else + { + if(activeTools != regularToolset) + { + activeTools = regularToolset; + notifyActiveToolsChanged(); + } + } + } + } +} + +vector<Tool*> GameModel::GetUnlistedTools() +{ + return extraElementTools; +} + +vector<Tool*> GameModel::GetToolList() +{ + return toolList; +} + +Menu * GameModel::GetActiveMenu() +{ + return activeMenu; +} + +Tool * GameModel::GetElementTool(int elementID) +{ + std::cout << elementID << std::endl; + for(std::vector<Tool*>::iterator iter = elementTools.begin(), end = elementTools.end(); iter != end; ++iter) + { + if((*iter)->GetToolID() == elementID) + return *iter; + } + return NULL; +} + +Tool * GameModel::GetActiveTool(int selection) +{ + return activeTools[selection]; +} + +void GameModel::SetActiveTool(int selection, Tool * tool) +{ + activeTools[selection] = tool; + notifyActiveToolsChanged(); +} + +vector<QuickOption*> GameModel::GetQuickOptions() +{ + return quickOptions; +} + +vector<Menu*> GameModel::GetMenuList() +{ + return menuList; +} + +SaveInfo * GameModel::GetSave() +{ + return currentSave; +} + +void GameModel::SetSave(SaveInfo * newSave) +{ + if(currentSave != newSave) + { + if(currentSave) + delete currentSave; + if(newSave == NULL) + currentSave = NULL; + else + currentSave = new SaveInfo(*newSave); + } + if(currentFile) + delete currentFile; + currentFile = NULL; + + if(currentSave && currentSave->GetGameSave()) + { + GameSave * saveData = currentSave->GetGameSave(); + SetPaused(saveData->paused | GetPaused()); + sim->gravityMode = saveData->gravityMode; + sim->air->airMode = saveData->airMode; + sim->legacy_enable = saveData->legacyEnable; + sim->water_equal_test = saveData->waterEEnabled; + if(saveData->gravityEnable) + sim->grav->start_grav_async(); + else + sim->grav->stop_grav_async(); + sim->SetEdgeMode(0); + sim->clear_sim(); + ren->ClearAccumulation(); + sim->Load(saveData); + } + notifySaveChanged(); + UpdateQuickOptions(); +} + +SaveFile * GameModel::GetSaveFile() +{ + return currentFile; +} + +void GameModel::SetSaveFile(SaveFile * newSave) +{ + if(currentFile != newSave) + { + if(currentFile) + delete currentFile; + if(newSave == NULL) + currentFile = NULL; + else + currentFile = new SaveFile(*newSave); + } + if (currentSave) + delete currentSave; + currentSave = NULL; + + if(newSave && newSave->GetGameSave()) + { + GameSave * saveData = newSave->GetGameSave(); + SetPaused(saveData->paused | GetPaused()); + sim->gravityMode = saveData->gravityMode; + sim->air->airMode = saveData->airMode; + sim->legacy_enable = saveData->legacyEnable; + sim->water_equal_test = saveData->waterEEnabled; + if(saveData->gravityEnable && !sim->grav->ngrav_enable) + { + sim->grav->start_grav_async(); + } + else if(!saveData->gravityEnable && sim->grav->ngrav_enable) + { + sim->grav->stop_grav_async(); + } + sim->SetEdgeMode(0); + sim->clear_sim(); + ren->ClearAccumulation(); + sim->Load(saveData); + } + + notifySaveChanged(); + UpdateQuickOptions(); +} + +Simulation * GameModel::GetSimulation() +{ + return sim; +} + +Renderer * GameModel::GetRenderer() +{ + return ren; +} + +User GameModel::GetUser() +{ + return currentUser; +} + +Tool * GameModel::GetLastTool() +{ + return lastTool; +} + +void GameModel::SetLastTool(Tool * newTool) +{ + if(lastTool != newTool) + { + lastTool = newTool; + notifyLastToolChanged(); + } +} + +void GameModel::SetZoomEnabled(bool enabled) +{ + ren->zoomEnabled = enabled; + notifyZoomChanged(); +} + +bool GameModel::GetZoomEnabled() +{ + return ren->zoomEnabled; +} + +void GameModel::SetZoomPosition(ui::Point position) +{ + ren->zoomScopePosition = position; + notifyZoomChanged(); +} + +ui::Point GameModel::GetZoomPosition() +{ + return ren->zoomScopePosition; +} + +void GameModel::SetZoomWindowPosition(ui::Point position) +{ + ren->zoomWindowPosition = position; + notifyZoomChanged(); +} + +ui::Point GameModel::GetZoomWindowPosition() +{ + return ren->zoomWindowPosition; +} + +void GameModel::SetZoomSize(int size) +{ + ren->zoomScopeSize = size; + notifyZoomChanged(); +} + +int GameModel::GetZoomSize() +{ + return ren->zoomScopeSize; +} + +void GameModel::SetZoomFactor(int factor) +{ + ren->ZFACTOR = factor; + notifyZoomChanged(); +} + +int GameModel::GetZoomFactor() +{ + return ren->ZFACTOR; +} + +void GameModel::SetActiveColourPreset(int preset) +{ + activeColourPreset = preset; + notifyColourActivePresetChanged(); +} + +int GameModel::GetActiveColourPreset() +{ + return activeColourPreset; +} + +void GameModel::SetPresetColour(ui::Colour colour) +{ + if(activeColourPreset >= 0 && activeColourPreset < colourPresets.size()) + { + colourPresets[activeColourPreset] = colour; + notifyColourPresetsChanged(); + } +} + +std::vector<ui::Colour> GameModel::GetColourPresets() +{ + return colourPresets; +} + +void GameModel::SetColourSelectorVisibility(bool visibility) +{ + if(colourSelector != visibility) + { + colourSelector = visibility; + notifyColourSelectorVisibilityChanged(); + } +} + +bool GameModel::GetColourSelectorVisibility() +{ + return colourSelector; +} + +void GameModel::SetColourSelectorColour(ui::Colour colour_) +{ + colour = colour_; + notifyColourSelectorColourChanged(); + + vector<Tool*> tools = GetMenuList()[SC_DECO]->GetToolList(); + for(int i = 0; i < tools.size(); i++) + { + ((DecorationTool*)tools[i])->Red = colour.Red; + ((DecorationTool*)tools[i])->Green = colour.Green; + ((DecorationTool*)tools[i])->Blue = colour.Blue; + ((DecorationTool*)tools[i])->Alpha = colour.Alpha; + } +} + +ui::Colour GameModel::GetColourSelectorColour() +{ + return colour; +} + +void GameModel::SetUser(User user) +{ + currentUser = user; + //Client::Ref().SetAuthUser(user); + notifyUserChanged(); +} + +void GameModel::SetPaused(bool pauseState) +{ + sim->sys_pause = pauseState?1:0; + notifyPausedChanged(); +} + +bool GameModel::GetPaused() +{ + return sim->sys_pause?true:false; +} + +void GameModel::SetDecoration(bool decorationState) +{ + ren->decorations_enable = decorationState?1:0; + notifyDecorationChanged(); + UpdateQuickOptions(); + if (decorationState) + SetInfoTip("Decorations Layer: On"); + else + SetInfoTip("Decorations Layer: Off"); +} + +bool GameModel::GetDecoration() +{ + return ren->decorations_enable?true:false; +} + +void GameModel::SetAHeatEnable(bool aHeat) +{ + sim->aheat_enable = aHeat; + UpdateQuickOptions(); + if (aHeat) + SetInfoTip("Ambient Heat: On"); + else + SetInfoTip("Ambient Heat: Off"); +} + +bool GameModel::GetAHeatEnable() +{ + return sim->aheat_enable; +} + +void GameModel::ShowGravityGrid(bool showGrid) +{ + ren->gravityFieldEnabled = showGrid; + if (showGrid) + SetInfoTip("Gravity Grid: On"); + else + SetInfoTip("Gravity Grid: Off"); +} + +bool GameModel::GetGravityGrid() +{ + return ren->gravityFieldEnabled; +} + +void GameModel::FrameStep(int frames) +{ + sim->framerender += frames; +} + +void GameModel::ClearSimulation() +{ + //Load defaults + sim->gravityMode = 0; + sim->air->airMode = 0; + sim->legacy_enable = false; + sim->water_equal_test = false; + sim->SetEdgeMode(edgeMode); + + sim->clear_sim(); + ren->ClearAccumulation(); + + notifySaveChanged(); + UpdateQuickOptions(); +} + +void GameModel::SetStamp(GameSave * save) +{ + if(stamp != save) + { + if(stamp) + delete stamp; + if(save) + stamp = new GameSave(*save); + else + stamp = NULL; + } +} + +void GameModel::SetPlaceSave(GameSave * save) +{ + if(save != placeSave) + { + if(placeSave) + delete placeSave; + if(save) + placeSave = new GameSave(*save); + else + placeSave = NULL; + } + notifyPlaceSaveChanged(); +} + +void GameModel::AddStamp(GameSave * save) +{ + if(stamp) + delete stamp; + stamp = save; + Client::Ref().AddStamp(save); +} + +void GameModel::SetClipboard(GameSave * save) +{ + if(clipboard) + delete clipboard; + clipboard = save; +} + +GameSave * GameModel::GetClipboard() +{ + return clipboard; +} + +GameSave * GameModel::GetPlaceSave() +{ + return placeSave; +} + +GameSave * GameModel::GetStamp() +{ + return stamp; +} + +void GameModel::Log(string message) +{ + consoleLog.push_front(message); + if(consoleLog.size()>100) + consoleLog.pop_back(); + notifyLogChanged(message); +} + +deque<string> GameModel::GetLog() +{ + return consoleLog; +} + +std::vector<Notification*> GameModel::GetNotifications() +{ + return notifications; +} + +void GameModel::AddNotification(Notification * notification) +{ + notifications.push_back(notification); + notifyNotificationsChanged(); +} + +void GameModel::RemoveNotification(Notification * notification) +{ + for(std::vector<Notification*>::iterator iter = notifications.begin(); iter != notifications.end(); ++iter) + { + if(*iter == notification) + { + delete *iter; + notifications.erase(iter); + break; + } + } + notifyNotificationsChanged(); +} + +void GameModel::SetToolTip(std::string text) +{ + toolTip = text; + notifyToolTipChanged(); +} + +void GameModel::SetInfoTip(std::string text) +{ + infoTip = text; + notifyInfoTipChanged(); +} + +std::string GameModel::GetToolTip() +{ + return toolTip; +} + +std::string GameModel::GetInfoTip() +{ + return infoTip; +} + +void GameModel::notifyNotificationsChanged() +{ + for(std::vector<GameView*>::iterator iter = observers.begin(); iter != observers.end(); ++iter) + { + (*iter)->NotifyNotificationsChanged(this); + } +} + +void GameModel::notifyColourPresetsChanged() +{ + for(std::vector<GameView*>::iterator iter = observers.begin(); iter != observers.end(); ++iter) + { + (*iter)->NotifyColourPresetsChanged(this); + } +} + +void GameModel::notifyColourActivePresetChanged() +{ + for(std::vector<GameView*>::iterator iter = observers.begin(); iter != observers.end(); ++iter) + { + (*iter)->NotifyColourActivePresetChanged(this); + } +} + +void GameModel::notifyColourSelectorColourChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyColourSelectorColourChanged(this); + } +} + +void GameModel::notifyColourSelectorVisibilityChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyColourSelectorVisibilityChanged(this); + } +} + +void GameModel::notifyRendererChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyRendererChanged(this); + } +} + +void GameModel::notifySaveChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifySaveChanged(this); + } +} + +void GameModel::notifySimulationChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifySimulationChanged(this); + } +} + +void GameModel::notifyPausedChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyPausedChanged(this); + } +} + +void GameModel::notifyDecorationChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + //observers[i]->NotifyPausedChanged(this); + } +} + +void GameModel::notifyBrushChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyBrushChanged(this); + } +} + +void GameModel::notifyMenuListChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyMenuListChanged(this); + } +} + +void GameModel::notifyToolListChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyToolListChanged(this); + } +} + +void GameModel::notifyActiveToolsChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyActiveToolsChanged(this); + } +} + +void GameModel::notifyUserChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyUserChanged(this); + } +} + +void GameModel::notifyZoomChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyZoomChanged(this); + } +} + +void GameModel::notifyPlaceSaveChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyPlaceSaveChanged(this); + } +} + +void GameModel::notifyLogChanged(string entry) +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyLogChanged(this, entry); + } +} + +void GameModel::notifyInfoTipChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyInfoTipChanged(this); + } +} + +void GameModel::notifyToolTipChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyToolTipChanged(this); + } +} + +void GameModel::notifyQuickOptionsChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyQuickOptionsChanged(this); + } +} + +void GameModel::notifyLastToolChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyLastToolChanged(this); + } +} diff --git a/src/gui/game/GameModel.h b/src/gui/game/GameModel.h new file mode 100644 index 0000000..0ade162 --- /dev/null +++ b/src/gui/game/GameModel.h @@ -0,0 +1,206 @@ +#ifndef GAMEMODEL_H +#define GAMEMODEL_H + +#include <vector> +#include <deque> +#include "client/SaveInfo.h" +#include "simulation/Simulation.h" +#include "gui/interface/Colour.h" +#include "graphics/Renderer.h" +#include "GameView.h" +#include "GameController.h" +#include "Brush.h" +#include "client/User.h" +#include "Notification.h" + +#include "Tool.h" +#include "Menu.h" + +using namespace std; + +class GameView; +class GameController; +class Simulation; +class Renderer; + +class QuickOption; +class ToolSelection +{ +public: + enum + { + ToolPrimary, ToolSecondary, ToolTertiary + }; +}; + +class GameModel +{ +private: + vector<Notification*> notifications; + //int clipboardSize; + //unsigned char * clipboardData; + GameSave * stamp; + GameSave * clipboard; + GameSave * placeSave; + deque<string> consoleLog; + vector<GameView*> observers; + vector<Tool*> toolList; + + //All tools that are associated with elements + vector<Tool*> elementTools; + //Tools that are present in elementTools, but don't have an associated menu and need to be freed manually + vector<Tool*> extraElementTools; + + vector<Menu*> menuList; + vector<QuickOption*> quickOptions; + Menu * activeMenu; + int currentBrush; + vector<Brush *> brushList; + SaveInfo * currentSave; + SaveFile * currentFile; + Simulation * sim; + Renderer * ren; + Tool * lastTool; + Tool ** activeTools; + Tool * decoToolset[3]; + Tool * regularToolset[3]; + User currentUser; + float toolStrength; + std::deque<Snapshot*> history; + + int activeColourPreset; + std::vector<ui::Colour> colourPresets; + bool colourSelector; + ui::Colour colour; + + int edgeMode; + + std::string infoTip; + std::string toolTip; + //bool zoomEnabled; + void notifyRendererChanged(); + void notifySimulationChanged(); + void notifyPausedChanged(); + void notifyDecorationChanged(); + void notifySaveChanged(); + void notifyBrushChanged(); + void notifyMenuListChanged(); + void notifyToolListChanged(); + void notifyActiveToolsChanged(); + void notifyUserChanged(); + void notifyZoomChanged(); + void notifyClipboardChanged(); + void notifyPlaceSaveChanged(); + void notifyColourSelectorColourChanged(); + void notifyColourSelectorVisibilityChanged(); + void notifyColourPresetsChanged(); + void notifyColourActivePresetChanged(); + void notifyNotificationsChanged(); + void notifyLogChanged(string entry); + void notifyInfoTipChanged(); + void notifyToolTipChanged(); + void notifyQuickOptionsChanged(); + void notifyLastToolChanged(); +public: + GameModel(); + ~GameModel(); + + Tool * GetToolFromIdentifier(std::string identifier); + + void SetEdgeMode(int edgeMode); + int GetEdgeMode(); + + void SetActiveColourPreset(int preset); + int GetActiveColourPreset(); + + void SetPresetColour(ui::Colour colour); + + std::vector<ui::Colour> GetColourPresets(); + + void SetColourSelectorVisibility(bool visibility); + bool GetColourSelectorVisibility(); + + void SetColourSelectorColour(ui::Colour colour); + ui::Colour GetColourSelectorColour(); + + void SetToolTip(std::string text); + void SetInfoTip(std::string text); + std::string GetToolTip(); + std::string GetInfoTip(); + + void BuildMenus(); + void BuildQuickOptionMenu(GameController * controller); + + std::deque<Snapshot*> GetHistory(); + void SetHistory(std::deque<Snapshot*> newHistory); + + void UpdateQuickOptions(); + + void SetToolStrength(float value); + float GetToolStrength(); + + Tool * GetLastTool(); + void SetLastTool(Tool * newTool); + + void SetVote(int direction); + SaveInfo * GetSave(); + SaveFile * GetSaveFile(); + Brush * GetBrush(); + void SetSave(SaveInfo * newSave); + void SetSaveFile(SaveFile * newSave); + void AddObserver(GameView * observer); + + //Get an element tool from an element ID + Tool * GetElementTool(int elementID); + + Tool * GetActiveTool(int selection); + void SetActiveTool(int selection, Tool * tool); + + bool GetPaused(); + void SetPaused(bool pauseState); + bool GetDecoration(); + void SetDecoration(bool decorationState); + bool GetAHeatEnable(); + void SetAHeatEnable(bool aHeat); + bool GetGravityGrid(); + void ShowGravityGrid(bool showGrid); + void ClearSimulation(); + vector<Menu*> GetMenuList(); + vector<Tool*> GetUnlistedTools(); + vector<Tool*> GetToolList(); + vector<QuickOption*> GetQuickOptions(); + void SetActiveMenu(Menu * menu); + Menu * GetActiveMenu(); + void FrameStep(int frames); + User GetUser(); + void SetUser(User user); + void SetBrush(int i); + int GetBrushID(); + Simulation * GetSimulation(); + Renderer * GetRenderer(); + void SetZoomEnabled(bool enabled); + bool GetZoomEnabled(); + void SetZoomSize(int size); + int GetZoomSize(); + void SetZoomFactor(int factor); + int GetZoomFactor(); + void SetZoomPosition(ui::Point position); + ui::Point GetZoomPosition(); + void SetZoomWindowPosition(ui::Point position); + ui::Point GetZoomWindowPosition(); + void SetStamp(GameSave * newStamp); + void AddStamp(GameSave * save); + void SetClipboard(GameSave * save); + void SetPlaceSave(GameSave * save); + void Log(string message); + deque<string> GetLog(); + GameSave * GetClipboard(); + GameSave * GetStamp(); + GameSave * GetPlaceSave(); + + std::vector<Notification*> GetNotifications(); + void AddNotification(Notification * notification); + void RemoveNotification(Notification * notification); +}; + +#endif // GAMEMODEL_H diff --git a/src/gui/game/GameModelException.h b/src/gui/game/GameModelException.h new file mode 100644 index 0000000..c95f7e6 --- /dev/null +++ b/src/gui/game/GameModelException.h @@ -0,0 +1,19 @@ +#ifndef GAMEMODELEXCEPTION_H_ +#define GAMEMODELEXCEPTION_H_ + +#include <string> +#include <exception> +using namespace std; + +struct GameModelException: public exception { + string message; +public: + GameModelException(string message_): message(message_) {} + const char * what() const throw() + { + return message.c_str(); + } + ~GameModelException() throw() {}; +}; + +#endif /* GAMEMODELEXCEPTION_H_ */ diff --git a/src/gui/game/GameView.cpp b/src/gui/game/GameView.cpp new file mode 100644 index 0000000..c11a59d --- /dev/null +++ b/src/gui/game/GameView.cpp @@ -0,0 +1,2140 @@ +#include <sstream> +#include <iomanip> + +#include "Config.h" +#include "gui/Style.h" +#include "GameView.h" +#include "graphics/Graphics.h" +#include "gui/interface/Window.h" +#include "gui/interface/Button.h" +#include "gui/interface/Colour.h" +#include "gui/interface/Keys.h" +#include "gui/interface/Slider.h" +#include "gui/search/Thumbnail.h" +#include "simulation/SaveRenderer.h" +#include "simulation/SimulationData.h" +#include "gui/dialogues/ConfirmPrompt.h" +#include "Format.h" +#include "QuickOption.h" +#include "IntroText.h" + + +class SplitButton; +class SplitButtonAction +{ +public: + virtual void ActionCallbackLeft(ui::Button * sender) {} + virtual void ActionCallbackRight(ui::Button * sender) {} + virtual ~SplitButtonAction() {} +}; +class SplitButton : public ui::Button +{ +private: + bool rightDown; + bool leftDown; + bool showSplit; + int splitPosition; + std::string toolTip2; + SplitButtonAction * splitActionCallback; +public: + SplitButton(ui::Point position, ui::Point size, std::string buttonText, std::string toolTip, std::string toolTip2, int split) : + Button(position, size, buttonText, toolTip), + toolTip2(toolTip2), + splitPosition(split), + splitActionCallback(NULL), + showSplit(true) + { + + } + bool GetShowSplit() { return showSplit; } + void SetShowSplit(bool split) { showSplit = split; } + SplitButtonAction * GetSplitActionCallback() { return splitActionCallback; } + void SetSplitActionCallback(SplitButtonAction * newAction) { splitActionCallback = newAction; } + virtual void OnMouseUnclick(int x, int y, unsigned int button) + { + if(isButtonDown) + { + if(leftDown) + DoLeftAction(); + else if(rightDown) + DoRightAction(); + } + ui::Button::OnMouseUnclick(x, y, button); + + } + virtual void OnMouseMovedInside(int x, int y, int dx, int dy) + { + if(x >= splitPosition || !showSplit) + { + if(toolTip.length()>0 && GetParentWindow()) + { + GetParentWindow()->ToolTip(this, ui::Point(x, y), toolTip2); + } + } + else if(x < splitPosition) + { + if(toolTip2.length()>0 && GetParentWindow()) + { + GetParentWindow()->ToolTip(this, ui::Point(x, y), toolTip); + } + } + } + virtual void OnMouseEnter(int x, int y) + { + isMouseInside = true; + if(!Enabled) + return; + if(x >= splitPosition || !showSplit) + { + if(toolTip.length()>0 && GetParentWindow()) + { + GetParentWindow()->ToolTip(this, ui::Point(x, y), toolTip2); + } + } + else if(x < splitPosition) + { + if(toolTip2.length()>0 && GetParentWindow()) + { + GetParentWindow()->ToolTip(this, ui::Point(x, y), toolTip); + } + } + } + virtual void TextPosition() + { + ui::Button::TextPosition(); + textPosition.X += 3; + } + virtual void OnMouseClick(int x, int y, unsigned int button) + { + ui::Button::OnMouseClick(x, y, button); + rightDown = false; + leftDown = false; + if(x >= splitPosition) + rightDown = true; + else if(x < splitPosition) + leftDown = true; + } + void DoRightAction() + { + if(!Enabled) + return; + if(splitActionCallback) + splitActionCallback->ActionCallbackRight(this); + } + void DoLeftAction() + { + if(!Enabled) + return; + if(splitActionCallback) + splitActionCallback->ActionCallbackLeft(this); + } + void Draw(const ui::Point& screenPos) + { + ui::Button::Draw(screenPos); + Graphics * g = ui::Engine::Ref().g; + drawn = true; + + if(showSplit) + g->draw_line(splitPosition+screenPos.X, screenPos.Y+1, splitPosition+screenPos.X, screenPos.Y+Size.Y-2, 180, 180, 180, 255); + } + virtual ~SplitButton() + { + if(splitActionCallback) + delete splitActionCallback; + } +}; + + +GameView::GameView(): + ui::Window(ui::Point(0, 0), ui::Point(XRES+BARSIZE, YRES+MENUSIZE)), + pointQueue(queue<ui::Point>()), + isMouseDown(false), + ren(NULL), + activeBrush(NULL), + currentMouse(0, 0), + toolIndex(0), + zoomEnabled(false), + zoomCursorFixed(false), + drawPoint1(0, 0), + drawPoint2(0, 0), + drawMode(DrawPoints), + drawModeReset(false), + selectMode(SelectNone), + selectPoint1(0, 0), + selectPoint2(0, 0), + placeSaveThumb(NULL), + mousePosition(0, 0), + lastOffset(0), + drawSnap(false), + toolTip(""), + infoTip(""), + infoTipPresence(0), + buttonTipShow(0), + toolTipPosition(-1, -1), + shiftBehaviour(false), + ctrlBehaviour(false), + altBehaviour(false), + showHud(true), + showDebug(false), + introText(2048), + introTextMessage(introTextData), + wallBrush(false), + doScreenshot(false), + recording(false), + screenshotIndex(0), + recordingIndex(0), + toolTipPresence(0), + currentSaveType(0), + lastLogEntry(0.0f), + lastMenu(NULL) +{ + + int currentX = 1; + //Set up UI + class SearchAction : public ui::ButtonAction + { + GameView * v; + public: + SearchAction(GameView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + if(v->CtrlBehaviour()) + v->c->OpenLocalBrowse(); + else + v->c->OpenSearch(); + } + }; + + scrollBar = new ui::Button(ui::Point(0,YRES+21), ui::Point(XRES, 2), ""); + scrollBar->Appearance.BackgroundInactive = ui::Colour(255, 255, 255); + scrollBar->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; + scrollBar->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(scrollBar); + + searchButton = new ui::Button(ui::Point(currentX, Size.Y-16), ui::Point(17, 15), "", "Find & open a simulation"); //Open + searchButton->SetIcon(IconOpen); + currentX+=18; + searchButton->SetTogglable(false); + searchButton->SetActionCallback(new SearchAction(this)); + AddComponent(searchButton); + + class ReloadAction : public ui::ButtonAction + { + GameView * v; + public: + ReloadAction(GameView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->ReloadSim(); + } + void AltActionCallback(ui::Button * sender) + { + v->c->OpenSavePreview(); + } + }; + reloadButton = new ui::Button(ui::Point(currentX, Size.Y-16), ui::Point(17, 15), "", "Reload the simulation"); + reloadButton->SetIcon(IconReload); + reloadButton->Appearance.Margin.Left+=2; + currentX+=18; + reloadButton->SetActionCallback(new ReloadAction(this)); + AddComponent(reloadButton); + + class SaveSimulationAction : public SplitButtonAction + { + GameView * v; + public: + SaveSimulationAction(GameView * _v) { v = _v; } + void ActionCallbackRight(ui::Button * sender) + { + if(v->CtrlBehaviour()) + v->c->OpenLocalSaveWindow(false); + else + v->c->OpenSaveWindow(); + } + void ActionCallbackLeft(ui::Button * sender) + { + if(v->CtrlBehaviour()) + v->c->OpenLocalSaveWindow(true); + else + v->c->SaveAsCurrent(); + } + }; + saveSimulationButton = new SplitButton(ui::Point(currentX, Size.Y-16), ui::Point(150, 15), "[untitled simulation]", "Save game as current name", "Save game as new name", 19); + saveSimulationButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + saveSimulationButton->SetIcon(IconSave); + currentX+=151; + ((SplitButton*)saveSimulationButton)->SetSplitActionCallback(new SaveSimulationAction(this)); + AddComponent(saveSimulationButton); + + class UpVoteAction : public ui::ButtonAction + { + GameView * v; + public: + UpVoteAction(GameView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->Vote(1); + } + }; + upVoteButton = new ui::Button(ui::Point(currentX, Size.Y-16), ui::Point(15, 15), "", "Like this save"); + upVoteButton->SetIcon(IconVoteUp); + upVoteButton->Appearance.Margin.Top+=2; + upVoteButton->Appearance.Margin.Left+=2; + currentX+=14; + upVoteButton->SetActionCallback(new UpVoteAction(this)); + AddComponent(upVoteButton); + + class DownVoteAction : public ui::ButtonAction + { + GameView * v; + public: + DownVoteAction(GameView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->Vote(-1); + } + }; + downVoteButton = new ui::Button(ui::Point(currentX, Size.Y-16), ui::Point(15, 15), "", "Dislike this save"); + downVoteButton->SetIcon(IconVoteDown); + downVoteButton->Appearance.Margin.Bottom+=2; + downVoteButton->Appearance.Margin.Left+=2; + currentX+=16; + downVoteButton->SetActionCallback(new DownVoteAction(this)); + AddComponent(downVoteButton); + + class TagSimulationAction : public ui::ButtonAction + { + GameView * v; + public: + TagSimulationAction(GameView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->OpenTags(); + } + }; + tagSimulationButton = new ui::Button(ui::Point(currentX, Size.Y-16), ui::Point(251, 15), "[no tags set]", "Add simulation tags"); + tagSimulationButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + tagSimulationButton->SetIcon(IconTag); + currentX+=252; + tagSimulationButton->SetActionCallback(new TagSimulationAction(this)); + AddComponent(tagSimulationButton); + + class ClearSimAction : public ui::ButtonAction + { + GameView * v; + public: + ClearSimAction(GameView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->ClearSim(); + } + }; + clearSimButton = new ui::Button(ui::Point(Size.X-159, Size.Y-16), ui::Point(17, 15), "", "Erase everything"); + clearSimButton->SetIcon(IconNew); + clearSimButton->Appearance.Margin.Left+=2; + clearSimButton->SetActionCallback(new ClearSimAction(this)); + AddComponent(clearSimButton); + + class LoginAction : public ui::ButtonAction + { + GameView * v; + public: + LoginAction(GameView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->OpenLogin(); + } + }; + loginButton = new ui::Button(ui::Point(Size.X-141, Size.Y-16), ui::Point(92, 15), "[sign in]", "Sign into simulation server"); + loginButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + loginButton->SetIcon(IconLogin); + loginButton->SetActionCallback(new LoginAction(this)); + AddComponent(loginButton); + + class SimulationOptionAction : public ui::ButtonAction + { + GameView * v; + public: + SimulationOptionAction(GameView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->OpenOptions(); + } + }; + simulationOptionButton = new ui::Button(ui::Point(Size.X-48, Size.Y-16), ui::Point(15, 15), "", "Simulation options"); + simulationOptionButton->SetIcon(IconSimulationSettings); + simulationOptionButton->Appearance.Margin.Left+=2; + simulationOptionButton->SetActionCallback(new SimulationOptionAction(this)); + AddComponent(simulationOptionButton); + + class DisplayModeAction : public ui::ButtonAction + { + GameView * v; + public: + DisplayModeAction(GameView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->OpenRenderOptions(); + } + }; + displayModeButton = new ui::Button(ui::Point(Size.X-32, Size.Y-16), ui::Point(15, 15), "", "Renderer options"); + displayModeButton->SetIcon(IconRenderSettings); + displayModeButton->Appearance.Margin.Left+=2; + displayModeButton->SetActionCallback(new DisplayModeAction(this)); + AddComponent(displayModeButton); + + class PauseAction : public ui::ButtonAction + { + GameView * v; + public: + PauseAction(GameView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->SetPaused(sender->GetToggleState()); + } + }; + pauseButton = new ui::Button(ui::Point(Size.X-16, Size.Y-16), ui::Point(15, 15), "", "Pause/Resume the simulation"); //Pause + pauseButton->SetIcon(IconPause); + pauseButton->SetTogglable(true); + pauseButton->SetActionCallback(new PauseAction(this)); + AddComponent(pauseButton); + + class ElementSearchAction : public ui::ButtonAction + { + GameView * v; + public: + ElementSearchAction(GameView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->OpenElementSearch(); + } + }; + ui::Button * tempButton = new ui::Button(ui::Point(XRES+BARSIZE-16, YRES+MENUSIZE-32), ui::Point(15, 15), "\xE5", "Search for elements"); + tempButton->Appearance.Margin = ui::Border(0, 2, 3, 2); + tempButton->SetActionCallback(new ElementSearchAction(this)); + AddComponent(tempButton); + + class ColourPickerAction : public ui::ButtonAction + { + GameView * v; + public: + ColourPickerAction(GameView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->OpenColourPicker(); + } + }; + colourPicker = new ui::Button(ui::Point((XRES/2)-8, YRES+1), ui::Point(16, 16), "", "Pick Colour"); + colourPicker->SetActionCallback(new ColourPickerAction(this)); +} + +GameView::~GameView() +{ + if(!colourPicker->GetParentWindow()) + delete colourPicker; + + for(std::vector<ToolButton*>::iterator iter = colourPresets.begin(), end = colourPresets.end(); iter != end; ++iter) + { + ToolButton * button = *iter; + if(!button->GetParentWindow()) + { + delete button; + } + + } + + if(placeSaveThumb) + delete placeSaveThumb; +} + +class GameView::MenuAction: public ui::ButtonAction +{ + GameView * v; +public: + Menu * menu; + bool needsClick; + MenuAction(GameView * _v, Menu * menu_) + { + v = _v; + menu = menu_; + if (v->c->GetMenuList()[SC_DECO] == menu) + needsClick = true; + else + needsClick = false; + } + void MouseEnterCallback(ui::Button * sender) + { + if(!needsClick && !ui::Engine::Ref().GetMouseButton()) + v->c->SetActiveMenu(menu); + } + void ActionCallback(ui::Button * sender) + { + if (needsClick) + v->c->SetActiveMenu(menu); + else + MouseEnterCallback(sender); + } +}; + +class GameView::OptionAction: public ui::ButtonAction +{ + QuickOption * option; +public: + OptionAction(QuickOption * _option) { option = _option; } + void ActionCallback(ui::Button * sender) + { + option->Perform(); + } +}; + +class GameView::OptionListener: public QuickOptionListener +{ + ui::Button * button; +public: + OptionListener(ui::Button * _button) { button = _button; } + virtual void OnValueChanged(QuickOption * option) + { + switch(option->GetType()) + { + case QuickOption::Toggle: + button->SetTogglable(true); + button->SetToggleState(option->GetToggle()); + } + } +}; + +class GameView::ToolAction: public ui::ButtonAction +{ + GameView * v; +public: + Tool * tool; + ToolAction(GameView * _v, Tool * tool_) { v = _v; tool = tool_; } + void ActionCallback(ui::Button * sender_) + { + ToolButton *sender = (ToolButton*)sender_; + if(sender->GetSelectionState() >= 0 && sender->GetSelectionState() <= 2) + v->c->SetActiveTool(sender->GetSelectionState(), tool); + } +}; + +void GameView::NotifyQuickOptionsChanged(GameModel * sender) +{ + for(int i = 0; i < quickOptionButtons.size(); i++) + { + RemoveComponent(quickOptionButtons[i]); + delete quickOptionButtons[i]; + } + + int currentY = 1; + vector<QuickOption*> optionList = sender->GetQuickOptions(); + for(vector<QuickOption*>::iterator iter = optionList.begin(), end = optionList.end(); iter != end; ++iter) + { + QuickOption * option = *iter; + ui::Button * tempButton = new ui::Button(ui::Point(XRES+BARSIZE-16, currentY), ui::Point(15, 15), option->GetIcon(), option->GetDescription()); + //tempButton->Appearance.Margin = ui::Border(0, 2, 3, 2); + tempButton->SetTogglable(true); + tempButton->SetActionCallback(new OptionAction(option)); + option->AddListener(new OptionListener(tempButton)); + AddComponent(tempButton); + + quickOptionButtons.push_back(tempButton); + currentY += 16; + } +} + +void GameView::NotifyMenuListChanged(GameModel * sender) +{ + int currentY = YRES+MENUSIZE-48;//-(sender->GetMenuList().size()*16); + for(int i = 0; i < menuButtons.size(); i++) + { + RemoveComponent(menuButtons[i]); + delete menuButtons[i]; + } + menuButtons.clear(); + for(int i = 0; i < toolButtons.size(); i++) + { + RemoveComponent(toolButtons[i]); + delete toolButtons[i]; + } + toolButtons.clear(); + vector<Menu*> menuList = sender->GetMenuList(); + for(vector<Menu*>::reverse_iterator iter = menuList.rbegin(), end = menuList.rend(); iter != end; ++iter) + { + std::string tempString = ""; + Menu * item = *iter; + tempString += item->GetIcon(); + ui::Button * tempButton = new ui::Button(ui::Point(XRES+BARSIZE-16, currentY), ui::Point(15, 15), tempString, item->GetDescription()); + tempButton->Appearance.Margin = ui::Border(0, 2, 3, 2); + tempButton->SetTogglable(true); + tempButton->SetActionCallback(new MenuAction(this, item)); + currentY-=16; + AddComponent(tempButton); + menuButtons.push_back(tempButton); + } +} + +void GameView::SetSample(SimulationSample sample) +{ + this->sample = sample; +} + +void GameView::SetHudEnable(bool hudState) +{ + showHud = hudState; +} + +ui::Point GameView::GetMousePosition() +{ + return mousePosition; +} + +void GameView::NotifyActiveToolsChanged(GameModel * sender) +{ + for(int i = 0; i < toolButtons.size(); i++) + { + Tool * tool = ((ToolAction*)toolButtons[i]->GetActionCallback())->tool; + if(sender->GetActiveTool(0) == tool) + { + toolButtons[i]->SetSelectionState(0); //Primary + c->ActiveToolChanged(0, tool); + } + else if(sender->GetActiveTool(1) == tool) + { + toolButtons[i]->SetSelectionState(1); //Secondary + c->ActiveToolChanged(1, tool); + } + else if(sender->GetActiveTool(2) == tool) + { + toolButtons[i]->SetSelectionState(2); //Tertiary + c->ActiveToolChanged(2, tool); + } + else + { + toolButtons[i]->SetSelectionState(-1); + } + } +} + +void GameView::NotifyLastToolChanged(GameModel * sender) +{ + if(sender->GetLastTool() && sender->GetLastTool()->GetResolution() == CELL) + { + wallBrush = true; + } + else + { + wallBrush = false; + } +} + +void GameView::NotifyToolListChanged(GameModel * sender) +{ + //int currentY = YRES+MENUSIZE-36; + lastOffset = 0; + int currentX = XRES+BARSIZE-56; + int totalColour; + for(int i = 0; i < menuButtons.size(); i++) + { + if(((MenuAction*)menuButtons[i]->GetActionCallback())->menu==sender->GetActiveMenu()) + { + menuButtons[i]->SetToggleState(true); + } + else + { + menuButtons[i]->SetToggleState(false); + } + } + for(int i = 0; i < toolButtons.size(); i++) + { + RemoveComponent(toolButtons[i]); + delete toolButtons[i]; + } + toolButtons.clear(); + vector<Tool*> toolList = sender->GetToolList(); + for(int i = 0; i < toolList.size(); i++) + { + //ToolButton * tempButton = new ToolButton(ui::Point(XRES+1, currentY), ui::Point(28, 15), toolList[i]->GetName()); + VideoBuffer * tempTexture = toolList[i]->GetTexture(26, 14); + ToolButton * tempButton; + + if(tempTexture) + tempButton = new ToolButton(ui::Point(currentX, YRES+1), ui::Point(30, 18), "", toolList[i]->GetDescription()); + else + tempButton = new ToolButton(ui::Point(currentX, YRES+1), ui::Point(30, 18), toolList[i]->GetName(), toolList[i]->GetDescription()); + + //currentY -= 17; + currentX -= 31; + tempButton->SetActionCallback(new ToolAction(this, toolList[i])); + + tempButton->Appearance.SetTexture(tempTexture); + if(tempTexture) + delete tempTexture; + + tempButton->Appearance.BackgroundInactive = ui::Colour(toolList[i]->colRed, toolList[i]->colGreen, toolList[i]->colBlue); + + if(sender->GetActiveTool(0) == toolList[i]) + { + tempButton->SetSelectionState(0); //Primary + } + else if(sender->GetActiveTool(1) == toolList[i]) + { + tempButton->SetSelectionState(1); //Secondary + } + else if(sender->GetActiveTool(2) == toolList[i]) + { + tempButton->SetSelectionState(2); //Tertiary + } + + tempButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; + tempButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(tempButton); + toolButtons.push_back(tempButton); + } + if (sender->GetActiveMenu() != sender->GetMenuList()[SC_DECO]) + lastMenu = sender->GetActiveMenu(); +} + +void GameView::NotifyColourSelectorVisibilityChanged(GameModel * sender) +{ + for(std::vector<ToolButton*>::iterator iter = colourPresets.begin(), end = colourPresets.end(); iter != end; ++iter) + { + ToolButton * button = *iter; + RemoveComponent(button); + button->SetParentWindow(NULL); + } + + RemoveComponent(colourPicker); + colourPicker->SetParentWindow(NULL); + + if(sender->GetColourSelectorVisibility()) + { + for(std::vector<ToolButton*>::iterator iter = colourPresets.begin(), end = colourPresets.end(); iter != end; ++iter) + { + ToolButton * button = *iter; + AddComponent(button); + } + AddComponent(colourPicker); + } +} + +void GameView::NotifyColourPresetsChanged(GameModel * sender) +{ + class ColourPresetAction: public ui::ButtonAction + { + GameView * v; + public: + int preset; + ColourPresetAction(GameView * _v, int preset) : preset(preset) { v = _v; } + void ActionCallback(ui::Button * sender_) + { + ToolButton *sender = (ToolButton*)sender_; + if(sender->GetSelectionState() == 0) + { + v->c->SetActiveColourPreset(preset); + v->c->SetColour(sender->Appearance.BackgroundInactive); + } + else + sender->SetSelectionState(0); + } + }; + + + for(std::vector<ToolButton*>::iterator iter = colourPresets.begin(), end = colourPresets.end(); iter != end; ++iter) + { + ToolButton * button = *iter; + RemoveComponent(button); + delete button; + } + colourPresets.clear(); + + int currentX = 5; + std::vector<ui::Colour> colours = sender->GetColourPresets(); + int i = 0; + for(std::vector<ui::Colour>::iterator iter = colours.begin(), end = colours.end(); iter != end; ++iter) + { + ToolButton * tempButton = new ToolButton(ui::Point(currentX, YRES+1), ui::Point(30, 18), ""); + tempButton->Appearance.BackgroundInactive = *iter; + tempButton->SetActionCallback(new ColourPresetAction(this, i)); + + currentX += 31; + + if(sender->GetColourSelectorVisibility()) + AddComponent(tempButton); + colourPresets.push_back(tempButton); + + i++; + } + NotifyColourActivePresetChanged(sender); +} + +void GameView::NotifyColourActivePresetChanged(GameModel * sender) +{ + for(int i = 0; i < colourPresets.size(); i++) + { + if(sender->GetActiveColourPreset() == i) + { + colourPresets[i]->SetSelectionState(0); //Primary + } + else + { + colourPresets[i]->SetSelectionState(-1); + } + } +} + +void GameView::NotifyColourSelectorColourChanged(GameModel * sender) +{ + colourPicker->Appearance.BackgroundInactive = sender->GetColourSelectorColour(); + colourPicker->Appearance.BackgroundHover = sender->GetColourSelectorColour(); +} + +void GameView::NotifyRendererChanged(GameModel * sender) +{ + ren = sender->GetRenderer(); +} + +void GameView::NotifySimulationChanged(GameModel * sender) +{ + +} +void GameView::NotifyUserChanged(GameModel * sender) +{ + if(!sender->GetUser().ID) + { + loginButton->SetText("[sign in]"); + } + else + { + loginButton->SetText(sender->GetUser().Username); + } + NotifySaveChanged(sender); +} + + +void GameView::NotifyPausedChanged(GameModel * sender) +{ + pauseButton->SetToggleState(sender->GetPaused()); +} + +void GameView::NotifyToolTipChanged(GameModel * sender) +{ + toolTip = sender->GetToolTip(); +} + +void GameView::NotifyInfoTipChanged(GameModel * sender) +{ + infoTip = sender->GetInfoTip(); + infoTipPresence = 120; +} + +void GameView::NotifySaveChanged(GameModel * sender) +{ + if(sender->GetSave()) + { + if(introText > 50) + introText = 50; + + saveSimulationButton->SetText(sender->GetSave()->GetName()); + if(sender->GetSave()->GetUserName() == sender->GetUser().Username) + ((SplitButton*)saveSimulationButton)->SetShowSplit(true); + else + ((SplitButton*)saveSimulationButton)->SetShowSplit(false); + reloadButton->Enabled = true; + upVoteButton->Enabled = (sender->GetSave()->GetID() && sender->GetUser().ID && sender->GetSave()->GetVote()==0); + if(sender->GetSave()->GetID() && sender->GetUser().ID && sender->GetSave()->GetVote()==1) + upVoteButton->Appearance.BackgroundDisabled = (ui::Colour(0, 108, 10, 255)); + else + upVoteButton->Appearance.BackgroundDisabled = (ui::Colour(0, 0, 0)); + + downVoteButton->Enabled = upVoteButton->Enabled; + if(sender->GetSave()->GetID() && sender->GetUser().ID && sender->GetSave()->GetVote()==-1) + downVoteButton->Appearance.BackgroundDisabled = (ui::Colour(108, 0, 10, 255)); + else + downVoteButton->Appearance.BackgroundDisabled = (ui::Colour(0, 0, 0)); + + if (sender->GetUser().ID) + { + upVoteButton->Appearance.BorderDisabled = upVoteButton->Appearance.BorderInactive; + downVoteButton->Appearance.BorderDisabled = downVoteButton->Appearance.BorderInactive; + } + else + { + upVoteButton->Appearance.BorderDisabled = ui::Colour(100, 100, 100); + downVoteButton->Appearance.BorderDisabled = ui::Colour(100, 100, 100); + } + + tagSimulationButton->Enabled = (sender->GetSave()->GetID() && sender->GetUser().ID); + if(sender->GetSave()->GetID()) + { + std::stringstream tagsStream; + std::vector<string> tags = sender->GetSave()->GetTags(); + if(tags.size()) + { + for(int i = 0; i < tags.size(); i++) + { + tagsStream << sender->GetSave()->GetTags()[i]; + if(i < tags.size()-1) + tagsStream << " "; + } + tagSimulationButton->SetText(tagsStream.str()); + } + else + { + tagSimulationButton->SetText("[no tags set]"); + } + } + else + { + tagSimulationButton->SetText("[no tags set]"); + } + currentSaveType = 1; + } + else if (sender->GetSaveFile()) + { + if (ctrlBehaviour) + ((SplitButton*)saveSimulationButton)->SetShowSplit(true); + else + ((SplitButton*)saveSimulationButton)->SetShowSplit(false); + saveSimulationButton->SetText(sender->GetSaveFile()->GetDisplayName()); + reloadButton->Enabled = true; + upVoteButton->Enabled = false; + upVoteButton->Appearance.BackgroundDisabled = (ui::Colour(0, 0, 0)); + upVoteButton->Appearance.BorderDisabled = ui::Colour(100, 100, 100); + downVoteButton->Enabled = false; + upVoteButton->Appearance.BackgroundDisabled = (ui::Colour(0, 0, 0)); + downVoteButton->Appearance.BorderDisabled = ui::Colour(100, 100, 100); + tagSimulationButton->Enabled = false; + tagSimulationButton->SetText("[no tags set]"); + currentSaveType = 2; + } + else + { + ((SplitButton*)saveSimulationButton)->SetShowSplit(false); + saveSimulationButton->SetText("[untitled simulation]"); + reloadButton->Enabled = false; + upVoteButton->Enabled = false; + upVoteButton->Appearance.BackgroundDisabled = (ui::Colour(0, 0, 0)); + upVoteButton->Appearance.BorderDisabled = ui::Colour(100, 100, 100), + downVoteButton->Enabled = false; + upVoteButton->Appearance.BackgroundDisabled = (ui::Colour(0, 0, 0)); + downVoteButton->Appearance.BorderDisabled = ui::Colour(100, 100, 100), + tagSimulationButton->Enabled = false; + tagSimulationButton->SetText("[no tags set]"); + currentSaveType = 0; + } +} + +void GameView::NotifyBrushChanged(GameModel * sender) +{ + activeBrush = sender->GetBrush(); +} + +void GameView::screenshot() +{ + doScreenshot = true; +} + +void GameView::record() +{ + if(recording) + { + recording = false; + } + else + { + class RecordingConfirmation: public ConfirmDialogueCallback { + public: + GameView * v; + RecordingConfirmation(GameView * v): v(v) {} + virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) { + if (result == ConfirmPrompt::ResultOkay) + { + v->recording = true; + } + } + virtual ~RecordingConfirmation() { } + }; + new ConfirmPrompt("Recording", "You're about to start recording all drawn frames. This may use a load of hard disk space.", new RecordingConfirmation(this)); + } +} + +void GameView::setToolButtonOffset(int offset) +{ + int offset_ = offset; + offset = offset-lastOffset; + lastOffset = offset_; + + for(vector<ToolButton*>::iterator iter = toolButtons.begin(), end = toolButtons.end(); iter!=end; ++iter) + { + ToolButton * button = *iter; + button->Position.X -= offset; + if(button->Position.X <= 0 || (button->Position.X+button->Size.X) > XRES-2) { + button->Visible = false; + } else { + button->Visible = true; + } + } +} + +void GameView::OnMouseMove(int x, int y, int dx, int dy) +{ + mousePosition = c->PointTranslate(ui::Point(x, y)); + if(selectMode!=SelectNone) + { + if(selectMode==PlaceSave) + selectPoint1 = c->PointTranslate(ui::Point(x, y)); + if(selectPoint1.X!=-1) + selectPoint2 = c->PointTranslate(ui::Point(x, y)); + return; + } + currentMouse = ui::Point(x, y); + if(isMouseDown && drawMode == DrawPoints) + { + pointQueue.push(ui::Point(c->PointTranslate(ui::Point(x-dx, y-dy)))); + pointQueue.push(ui::Point(c->PointTranslate(ui::Point(x, y)))); + } +} + +void GameView::OnMouseDown(int x, int y, unsigned button) +{ + if(altBehaviour && !shiftBehaviour && !ctrlBehaviour) + button = BUTTON_MIDDLE; + if(selectMode!=SelectNone) + { + if(button==BUTTON_LEFT) + { + selectPoint1 = c->PointTranslate(ui::Point(x, y)); + selectPoint2 = selectPoint1; + } + return; + } + if(currentMouse.X >= 0 && currentMouse.X < XRES && currentMouse.Y >= 0 && currentMouse.Y < YRES && !(zoomEnabled && !zoomCursorFixed)) + { + if(button == BUTTON_LEFT) + toolIndex = 0; + if(button == BUTTON_RIGHT) + toolIndex = 1; + if(button == BUTTON_MIDDLE) + toolIndex = 2; + isMouseDown = true; + if(!pointQueue.size()) + c->HistorySnapshot(); + if(drawMode == DrawRect || drawMode == DrawLine) + { + drawPoint1 = c->PointTranslate(ui::Point(x, y)); + } + if(drawMode == DrawPoints) + { + pointQueue.push(ui::Point(c->PointTranslate(ui::Point(x, y)))); + } + } +} + +void GameView::OnMouseUp(int x, int y, unsigned button) +{ + if(selectMode!=SelectNone) + { + if(button==BUTTON_LEFT) + { + if(selectMode==PlaceSave) + { + if(placeSaveThumb) + { + int thumbX = selectPoint2.X - (placeSaveThumb->Width/2); + int thumbY = selectPoint2.Y - (placeSaveThumb->Height/2); + + if(thumbX<0) + thumbX = 0; + if(thumbX+(placeSaveThumb->Width)>=XRES) + thumbX = XRES-placeSaveThumb->Width; + + if(thumbY<0) + thumbY = 0; + if(thumbY+(placeSaveThumb->Height)>=YRES) + thumbY = YRES-placeSaveThumb->Height; + + c->PlaceSave(ui::Point(thumbX, thumbY)); + } + } + else + { + int x2 = (selectPoint1.X>selectPoint2.X)?selectPoint1.X:selectPoint2.X; + int y2 = (selectPoint1.Y>selectPoint2.Y)?selectPoint1.Y:selectPoint2.Y; + int x1 = (selectPoint2.X<selectPoint1.X)?selectPoint2.X:selectPoint1.X; + int y1 = (selectPoint2.Y<selectPoint1.Y)?selectPoint2.Y:selectPoint1.Y; + if(selectMode==SelectCopy) + c->CopyRegion(ui::Point(x1, y1), ui::Point(x2, y2)); + else if(selectMode==SelectCut) + c->CutRegion(ui::Point(x1, y1), ui::Point(x2, y2)); + else if(selectMode==SelectStamp) + c->StampRegion(ui::Point(x1, y1), ui::Point(x2, y2)); + } + } + currentMouse = ui::Point(x, y); + selectMode = SelectNone; + return; + } + + if(zoomEnabled && !zoomCursorFixed) + zoomCursorFixed = true; + else + { + if(isMouseDown) + { + isMouseDown = false; + if(drawMode == DrawRect || drawMode == DrawLine) + { + ui::Point finalDrawPoint2(0, 0); + drawPoint2 = c->PointTranslate(ui::Point(x, y)); + finalDrawPoint2 = drawPoint2; + + if(drawSnap && drawMode == DrawLine) + { + finalDrawPoint2 = lineSnapCoords(c->PointTranslate(drawPoint1), drawPoint2); + } + + if(drawSnap && drawMode == DrawRect) + { + finalDrawPoint2 = rectSnapCoords(c->PointTranslate(drawPoint1), drawPoint2); + } + + if(drawMode == DrawRect) + { + c->DrawRect(toolIndex, c->PointTranslate(drawPoint1), finalDrawPoint2); + } + if(drawMode == DrawLine) + { + c->DrawLine(toolIndex, c->PointTranslate(drawPoint1), finalDrawPoint2); + } + } + if(drawMode == DrawPoints) + { + c->ToolClick(toolIndex, c->PointTranslate(ui::Point(x, y))); + //pointQueue.push(ui::Point(x, y)); + } + if(drawModeReset) + { + drawModeReset = false; + drawMode = DrawPoints; + } + } + } +} + +void GameView::ExitPrompt() +{ + class ExitConfirmation: public ConfirmDialogueCallback { + public: + ExitConfirmation() {} + virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) { + if (result == ConfirmPrompt::ResultOkay) + { + ui::Engine::Ref().Exit(); + } + } + virtual ~ExitConfirmation() { } + }; + new ConfirmPrompt("You are about to quit", "Are you sure you want to exit the game?", new ExitConfirmation()); +} + +void GameView::ToolTip(ui::Component * sender, ui::Point mousePosition, std::string toolTip) +{ + if(sender->Position.Y > Size.Y-17) + { + buttonTip = toolTip; + buttonTipShow = 120; + } + else if(sender->Position.X > Size.X-BARSIZE)// < Size.Y-(quickOptionButtons.size()+1)*16) + { + this->toolTip = toolTip; + toolTipPosition = ui::Point(Size.X-27-Graphics::textwidth((char*)toolTip.c_str()), sender->Position.Y+3); + if(toolTipPosition.Y+10 > Size.Y-MENUSIZE) + toolTipPosition = ui::Point(Size.X-27-Graphics::textwidth((char*)toolTip.c_str()), Size.Y-MENUSIZE-10); + toolTipPresence = 120; + } + else + { + this->toolTip = toolTip; + toolTipPosition = ui::Point(Size.X-27-Graphics::textwidth((char*)toolTip.c_str()), Size.Y-MENUSIZE-10); + toolTipPresence = 160; + } +} + +void GameView::OnMouseWheel(int x, int y, int d) +{ + if(!d) + return; + if(selectMode!=SelectNone) + { + return; + } + if(zoomEnabled && !zoomCursorFixed) + { + c->AdjustZoomSize(d); + } + else + { + c->AdjustBrushSize(d, false, shiftBehaviour, ctrlBehaviour); + if(isMouseDown) + { + pointQueue.push(ui::Point(c->PointTranslate(ui::Point(x, y)))); + } + } +} + +void GameView::ToggleDebug() +{ + showDebug = !showDebug; + if (ren) + ren->debugLines = showDebug; +} + +void GameView::BeginStampSelection() +{ + selectMode = SelectStamp; + selectPoint1 = ui::Point(-1, -1); + infoTip = "\x0F\xEF\xEF\x10Select an area to create a stamp"; + infoTipPresence = 120; +} + +void GameView::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + if(introText > 50) + { + introText = 50; + } + + if(selectMode!=SelectNone) + { + if(selectMode==PlaceSave) + { + switch(key) + { + case KEY_RIGHT: + case 'd': + c->TranslateSave(ui::Point(1, 0)); + break; + case KEY_LEFT: + case 'a': + c->TranslateSave(ui::Point(-1, 0)); + break; + case KEY_UP: + case 'w': + c->TranslateSave(ui::Point(0, -1)); + break; + case KEY_DOWN: + case 's': + c->TranslateSave(ui::Point(0, 1)); + break; + case 'r': + if(shift) + { + //Flip + c->TransformSave(m2d_new(-1,0,0,1)); + } + else + { + //Rotate 90deg + c->TransformSave(m2d_new(0,1,-1,0)); + } + break; + } + } + if(key != ' ') + return; + } + switch(key) + { + case KEY_ALT: + drawSnap = true; + enableAltBehaviour(); + break; + case KEY_CTRL: + if(!isMouseDown) + { + if(drawModeReset) + drawModeReset = false; + else + drawPoint1 = currentMouse; + if(shift) + drawMode = DrawFill; + else + drawMode = DrawRect; + } + enableCtrlBehaviour(); + break; + case KEY_SHIFT: + if(!isMouseDown) + { + if(drawModeReset) + drawModeReset = false; + else + drawPoint1 = currentMouse; + if(ctrl) + drawMode = DrawFill; + else + drawMode = DrawLine; + } + enableShiftBehaviour(); + break; + case ' ': //Space + c->SetPaused(); + break; + case KEY_TAB: //Tab + c->ChangeBrush(); + break; + case 'z': + if(ctrl) + { + c->HistoryRestore(); + } + else + { + if (drawMode != DrawLine) + isMouseDown = false; + zoomCursorFixed = false; + c->SetZoomEnabled(true); + } + break; + case '`': + c->ShowConsole(); + break; + case 'p': + screenshot(); + break; + case 'r': + record(); + break; + case 'e': + c->OpenElementSearch(); + break; + case 'f': + c->FrameStep(); + break; + case 'g': + if (ctrl) + c->ShowGravityGrid(); + else if(shift) + c->AdjustGridSize(-1); + else + c->AdjustGridSize(1); + break; + case KEY_F1: + if(!introText) + introText = 8047; + else + introText = 0; + break; + case 'h': + if(ctrl) + { + if(!introText) + introText = 8047; + else + introText = 0; + } + else + showHud = !showHud; + break; + case 'b': + if(ctrl) + c->SetDecoration(); + else + if (colourPicker->GetParentWindow()) + c->SetActiveMenu(lastMenu); + else + { + c->SetDecoration(true); + c->SetPaused(true); + c->SetActiveMenu(c->GetMenuList()[SC_DECO]); + } + break; + case 'y': + c->SwitchAir(); + break; + case KEY_ESCAPE: + case 'q': + ExitPrompt(); + break; + case 'u': + c->ToggleAHeat(); + break; + case '=': + if(ctrl) + c->ResetSpark(); + else + c->ResetAir(); + break; + case 'c': + if(ctrl) + { + selectMode = SelectCopy; + selectPoint1 = ui::Point(-1, -1); + infoTip = "\x0F\xEF\xEF\x10Select an area to copy"; + infoTipPresence = 120; + } + break; + case 'x': + if(ctrl) + { + selectMode = SelectCut; + selectPoint1 = ui::Point(-1, -1); + infoTip = "\x0F\xEF\xEF\x10Select an area to cut"; + infoTipPresence = 120; + } + break; + case 'v': + if(ctrl) + { + c->LoadClipboard(); + selectPoint2 = mousePosition; + selectPoint1 = selectPoint2; + } + break; + case 'l': + c->LoadStamp(); + selectPoint2 = mousePosition; + selectPoint1 = selectPoint2; + isMouseDown = false; + drawMode = DrawPoints; + break; + case 'k': + selectPoint2 = ui::Point(-1, -1); + selectPoint1 = selectPoint2; + c->OpenStamps(); + break; + case ']': + if(zoomEnabled && !zoomCursorFixed) + c->AdjustZoomSize(1, !alt); + else + c->AdjustBrushSize(1, !alt, shiftBehaviour, ctrlBehaviour); + break; + case '[': + if(zoomEnabled && !zoomCursorFixed) + c->AdjustZoomSize(-1, !alt); + else + c->AdjustBrushSize(-1, !alt, shiftBehaviour, ctrlBehaviour); + break; + case 'i': + if(ctrl) + c->Install(); + else + c->InvertAirSim(); + break; + } + + if (shift && showDebug && key == '1') + c->LoadRenderPreset(10); + else if(key >= '0' && key <= '9') + { + c->LoadRenderPreset(key-'0'); + } +} + +void GameView::OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + if(ctrl && shift) + drawMode = DrawFill; + else if (ctrl) + drawMode = DrawRect; + else if (shift) + drawMode = DrawLine; + else if(!isMouseDown) + drawMode = DrawPoints; + else + drawModeReset = true; + switch(key) + { + case KEY_ALT: + drawSnap = false; + disableAltBehaviour(); + break; + case KEY_CTRL: + disableCtrlBehaviour(); + break; + case KEY_SHIFT: + disableShiftBehaviour(); + break; + case 'z': + if(!ctrl) + { + if(!zoomCursorFixed && !alt) + c->SetZoomEnabled(false); + } + break; + } +} + +void GameView::OnBlur() +{ + disableAltBehaviour(); + disableCtrlBehaviour(); + disableShiftBehaviour(); + isMouseDown = false; + drawMode = DrawPoints; +} + +void GameView::OnTick(float dt) +{ + if(selectMode==PlaceSave && !placeSaveThumb) + selectMode = SelectNone; + if(zoomEnabled && !zoomCursorFixed) + c->SetZoomPosition(currentMouse); + if(drawMode == DrawPoints) + { + if(isMouseDown) + { + pointQueue.push(ui::Point(c->PointTranslate(currentMouse))); + } + if(!pointQueue.empty()) + { + c->DrawPoints(toolIndex, pointQueue); + } + } + if(drawMode == DrawFill && isMouseDown) + { + c->DrawFill(toolIndex, c->PointTranslate(currentMouse)); + } + if(introText) + { + introText -= int(dt)>0?((int)dt < 5? dt:5):1; + if(introText < 0) + introText = 0; + } + if(infoTipPresence>0) + { + infoTipPresence -= int(dt)>0?int(dt):1; + if(infoTipPresence<0) + infoTipPresence = 0; + } + if(buttonTipShow>0) + { + buttonTipShow -= int(dt)>0?int(dt):1; + if(buttonTipShow<0) + buttonTipShow = 0; + } + if(toolTipPresence>0) + { + toolTipPresence -= int(dt)>0?int(dt):1; + if(toolTipPresence<0) + toolTipPresence = 0; + } + c->Update(); + if(lastLogEntry > -0.1f) + lastLogEntry -= 0.16*dt; +} + + +void GameView::DoMouseMove(int x, int y, int dx, int dy) +{ + if(c->MouseMove(x, y, dx, dy)) + Window::DoMouseMove(x, y, dx, dy); + + if(toolButtons.size()) + { + int totalWidth = (toolButtons[0]->Size.X+1)*toolButtons.size(); + int scrollSize = (int)(((float)(XRES-15))/((float)totalWidth) * ((float)XRES-15)); + if (scrollSize>XRES) + scrollSize = XRES; + if(totalWidth > XRES-15) + { + int mouseX = x; + if(mouseX > XRES) + mouseX = XRES; + + scrollBar->Position.X = (int)(((float)mouseX/((float)XRES-15))*(float)(XRES-scrollSize)); + + float overflow = totalWidth-(XRES-15), mouseLocation = float(XRES)/float(mouseX-(XRES)); + setToolButtonOffset(overflow/mouseLocation); + + //Ensure that mouseLeave events are make their way to the buttons should they move from underneith the mouse pointer + if(toolButtons[0]->Position.Y < y && toolButtons[0]->Position.Y+toolButtons[0]->Size.Y > y) + { + for(vector<ToolButton*>::iterator iter = toolButtons.begin(), end = toolButtons.end(); iter!=end; ++iter) + { + ToolButton * button = *iter; + if(button->Position.X < x && button->Position.X+button->Size.X > x) + button->OnMouseEnter(x, y); + else + button->OnMouseLeave(x, y); + } + } + } + else + { + scrollBar->Position.X = 0; + } + scrollBar->Size.X=scrollSize; + } +} + +void GameView::DoMouseDown(int x, int y, unsigned button) +{ + if(introText > 50) + introText = 50; + if(c->MouseDown(x, y, button)) + Window::DoMouseDown(x, y, button); +} + +void GameView::DoMouseUp(int x, int y, unsigned button) +{ + if(c->MouseUp(x, y, button)) + Window::DoMouseUp(x, y, button); +} + +void GameView::DoMouseWheel(int x, int y, int d) +{ + if(c->MouseWheel(x, y, d)) + Window::DoMouseWheel(x, y, d); +} + +void GameView::DoKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + if(c->KeyPress(key, character, shift, ctrl, alt)) + Window::DoKeyPress(key, character, shift, ctrl, alt); +} + +void GameView::DoKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + if(c->KeyRelease(key, character, shift, ctrl, alt)) + Window::DoKeyRelease(key, character, shift, ctrl, alt); +} + +void GameView::DoDraw() +{ + Window::DoDraw(); + c->Tick(); +} + +void GameView::NotifyNotificationsChanged(GameModel * sender) +{ + class NotificationButtonAction : public ui::ButtonAction + { + GameView * v; + Notification * notification; + public: + NotificationButtonAction(GameView * v, Notification * notification) : v(v), notification(notification) { } + void ActionCallback(ui::Button * sender) + { + notification->Action(); + //v->c->RemoveNotification(notification); + } + }; + class CloseNotificationButtonAction : public ui::ButtonAction + { + GameView * v; + Notification * notification; + public: + CloseNotificationButtonAction(GameView * v, Notification * notification) : v(v), notification(notification) { } + void ActionCallback(ui::Button * sender) + { + v->c->RemoveNotification(notification); + } + void AltActionCallback(ui::Button * sender) + { + v->c->RemoveNotification(notification); + } + }; + + for(std::vector<ui::Component*>::const_iterator iter = notificationComponents.begin(), end = notificationComponents.end(); iter != end; ++iter) { + ui::Component * cNotification = *iter; + RemoveComponent(cNotification); + delete cNotification; + } + notificationComponents.clear(); + + std::vector<Notification*> notifications = sender->GetNotifications(); + + int currentY = YRES-23; + for(std::vector<Notification*>::iterator iter = notifications.begin(), end = notifications.end(); iter != end; ++iter) + { + int width = (Graphics::textwidth((*iter)->Message.c_str()))+8; + ui::Button * tempButton = new ui::Button(ui::Point(XRES-width-22, currentY), ui::Point(width, 15), (*iter)->Message); + tempButton->SetActionCallback(new NotificationButtonAction(this, *iter)); + tempButton->Appearance.BorderInactive = style::Colour::WarningTitle; + tempButton->Appearance.TextInactive = style::Colour::WarningTitle; + tempButton->Appearance.BorderHover = ui::Colour(255, 175, 0); + tempButton->Appearance.TextHover = ui::Colour(255, 175, 0); + AddComponent(tempButton); + notificationComponents.push_back(tempButton); + + tempButton = new ui::Button(ui::Point(XRES-20, currentY), ui::Point(15, 15)); + tempButton->SetIcon(IconClose); + tempButton->SetActionCallback(new CloseNotificationButtonAction(this, *iter)); + tempButton->Appearance.Margin.Left+=2; + tempButton->Appearance.Margin.Top+=2; + tempButton->Appearance.BorderInactive = style::Colour::WarningTitle; + tempButton->Appearance.TextInactive = style::Colour::WarningTitle; + tempButton->Appearance.BorderHover = ui::Colour(255, 175, 0); + tempButton->Appearance.TextHover = ui::Colour(255, 175, 0); + AddComponent(tempButton); + notificationComponents.push_back(tempButton); + + currentY -= 17; + } +} + +void GameView::NotifyZoomChanged(GameModel * sender) +{ + zoomEnabled = sender->GetZoomEnabled(); +} + +void GameView::NotifyLogChanged(GameModel * sender, string entry) +{ + logEntries.push_front(entry); + lastLogEntry = 100.0f; + if(logEntries.size()>20) + logEntries.pop_back(); +} + +void GameView::NotifyPlaceSaveChanged(GameModel * sender) +{ + if(placeSaveThumb) + delete placeSaveThumb; + if(sender->GetPlaceSave()) + { + placeSaveThumb = SaveRenderer::Ref().Render(sender->GetPlaceSave()); + selectMode = PlaceSave; + selectPoint2 = mousePosition; + } + else + { + placeSaveThumb = NULL; + selectMode = SelectNone; + } +} + +void GameView::enableShiftBehaviour() +{ + if(!shiftBehaviour) + { + shiftBehaviour = true; + if(isMouseDown) + { + c->SetToolStrength(10.0f); + } + } +} + +void GameView::disableShiftBehaviour() +{ + if(shiftBehaviour) + { + shiftBehaviour = false; + if(!ctrlBehaviour) + c->SetToolStrength(1.0f); + else + c->SetToolStrength(.1f); + } +} + +void GameView::enableAltBehaviour() +{ + if(!altBehaviour) + { + altBehaviour = true; + } +} + +void GameView::disableAltBehaviour() +{ + if(altBehaviour) + { + altBehaviour = false; + } +} + +void GameView::enableCtrlBehaviour() +{ + if(!ctrlBehaviour) + { + ctrlBehaviour = true; + + //Show HDD save & load buttons + saveSimulationButton->Appearance.BackgroundInactive = saveSimulationButton->Appearance.BackgroundHover = ui::Colour(255, 255, 255); + saveSimulationButton->Appearance.TextInactive = saveSimulationButton->Appearance.TextHover = ui::Colour(0, 0, 0); + searchButton->Appearance.BackgroundInactive = searchButton->Appearance.BackgroundHover = ui::Colour(255, 255, 255); + searchButton->Appearance.TextInactive = searchButton->Appearance.TextHover = ui::Colour(0, 0, 0); + if (currentSaveType == 2) + ((SplitButton*)saveSimulationButton)->SetShowSplit(true); + if(isMouseDown) + { + if(!shiftBehaviour) + c->SetToolStrength(.1f); + else + c->SetToolStrength(10.0f); + } + } +} + +void GameView::disableCtrlBehaviour() +{ + if(ctrlBehaviour) + { + ctrlBehaviour = false; + + //Hide HDD save & load buttons + saveSimulationButton->Appearance.BackgroundInactive = ui::Colour(0, 0, 0); + saveSimulationButton->Appearance.BackgroundHover = ui::Colour(20, 20, 20); + saveSimulationButton->Appearance.TextInactive = saveSimulationButton->Appearance.TextHover = ui::Colour(255, 255, 255); + searchButton->Appearance.BackgroundInactive = ui::Colour(0, 0, 0); + searchButton->Appearance.BackgroundHover = ui::Colour(20, 20, 20); + searchButton->Appearance.TextInactive = searchButton->Appearance.TextHover = ui::Colour(255, 255, 255); + if (currentSaveType == 2) + ((SplitButton*)saveSimulationButton)->SetShowSplit(false); + if(!shiftBehaviour) + c->SetToolStrength(1.0f); + else + c->SetToolStrength(10.0f); + } +} + +void GameView::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + if(ren) + { + ren->clearScreen(1.0f); + ren->RenderBegin(); + ren->SetSample(c->PointTranslate(currentMouse).X, c->PointTranslate(currentMouse).Y); + if(selectMode == SelectNone && (!zoomEnabled || zoomCursorFixed) && activeBrush && currentMouse.X >= 0 && currentMouse.X < XRES && currentMouse.Y >= 0 && currentMouse.Y < YRES) + { + ui::Point finalCurrentMouse = c->PointTranslate(currentMouse); + ui::Point initialDrawPoint = drawPoint1; + + if(wallBrush) + { + finalCurrentMouse = c->NormaliseBlockCoord(finalCurrentMouse); + initialDrawPoint = c->NormaliseBlockCoord(initialDrawPoint); + } + + if(drawMode==DrawRect && isMouseDown) + { + if(drawSnap) + { + finalCurrentMouse = rectSnapCoords(c->PointTranslate(initialDrawPoint), finalCurrentMouse); + } + activeBrush->RenderRect(ren, c->PointTranslate(initialDrawPoint), finalCurrentMouse); + } + else if(drawMode==DrawLine && isMouseDown) + { + if(drawSnap) + { + finalCurrentMouse = lineSnapCoords(c->PointTranslate(initialDrawPoint), finalCurrentMouse); + } + activeBrush->RenderLine(ren, c->PointTranslate(initialDrawPoint), finalCurrentMouse); + } + else if(drawMode==DrawFill)// || altBehaviour) + { + activeBrush->RenderFill(ren, finalCurrentMouse); + } + if(drawMode == DrawPoints || drawMode==DrawLine || (drawMode == DrawRect && !isMouseDown)) + { + if(wallBrush) + { + ui::Point finalBrushRadius = c->NormaliseBlockCoord(activeBrush->GetRadius()); + ren->xor_line(finalCurrentMouse.X-finalBrushRadius.X, finalCurrentMouse.Y-finalBrushRadius.Y, finalCurrentMouse.X+finalBrushRadius.X+CELL-1, finalCurrentMouse.Y-finalBrushRadius.Y); + ren->xor_line(finalCurrentMouse.X-finalBrushRadius.X, finalCurrentMouse.Y+finalBrushRadius.Y+CELL-1, finalCurrentMouse.X+finalBrushRadius.X+CELL-1, finalCurrentMouse.Y+finalBrushRadius.Y+CELL-1); + + ren->xor_line(finalCurrentMouse.X-finalBrushRadius.X, finalCurrentMouse.Y-finalBrushRadius.Y+1, finalCurrentMouse.X-finalBrushRadius.X, finalCurrentMouse.Y+finalBrushRadius.Y+CELL-2); + ren->xor_line(finalCurrentMouse.X+finalBrushRadius.X+CELL-1, finalCurrentMouse.Y-finalBrushRadius.Y+1, finalCurrentMouse.X+finalBrushRadius.X+CELL-1, finalCurrentMouse.Y+finalBrushRadius.Y+CELL-2); + } + else + { + activeBrush->RenderPoint(ren, finalCurrentMouse); + } + } + } + + if(selectMode!=SelectNone) + { + if(selectMode==PlaceSave) + { + if(placeSaveThumb && selectPoint2.X!=-1) + { + int thumbX = selectPoint2.X - (placeSaveThumb->Width/2); + int thumbY = selectPoint2.Y - (placeSaveThumb->Height/2); + + ui::Point thumbPos = c->NormaliseBlockCoord(ui::Point(thumbX, thumbY)); + + if(thumbPos.X<0) + thumbPos.X = 0; + if(thumbPos.X+(placeSaveThumb->Width)>=XRES) + thumbPos.X = XRES-placeSaveThumb->Width; + + if(thumbPos.Y<0) + thumbPos.Y = 0; + if(thumbPos.Y+(placeSaveThumb->Height)>=YRES) + thumbPos.Y = YRES-placeSaveThumb->Height; + + ren->draw_image(placeSaveThumb, thumbPos.X, thumbPos.Y, 128); + + ren->xor_rect(thumbPos.X, thumbPos.Y, placeSaveThumb->Width, placeSaveThumb->Width); + } + } + else + { + if(selectPoint1.X==-1) + { + ren->fillrect(0, 0, XRES, YRES, 0, 0, 0, 100); + } + else + { + int x2 = (selectPoint1.X>selectPoint2.X)?selectPoint1.X:selectPoint2.X; + int y2 = (selectPoint1.Y>selectPoint2.Y)?selectPoint1.Y:selectPoint2.Y; + int x1 = (selectPoint2.X<selectPoint1.X)?selectPoint2.X:selectPoint1.X; + int y1 = (selectPoint2.Y<selectPoint1.Y)?selectPoint2.Y:selectPoint1.Y; + + if(x2>XRES-1) + x2 = XRES-1; + if(y2>YRES-1) + y2 = YRES-1; + + ren->fillrect(0, 0, XRES, y1, 0, 0, 0, 100); + ren->fillrect(0, y2+1, XRES, YRES-y2-1, 0, 0, 0, 100); + + ren->fillrect(0, y1, x1, (y2-y1)+1, 0, 0, 0, 100); + ren->fillrect(x2+1, y1, XRES-x2-1, (y2-y1)+1, 0, 0, 0, 100); + + ren->xor_rect(x1, y1, (x2-x1)+1, (y2-y1)+1); + } + } + } + + ren->RenderEnd(); + + if(doScreenshot) + { + VideoBuffer screenshot(ren->DumpFrame()); + std::vector<char> data = format::VideoBufferToPNG(screenshot); + + std::stringstream filename; + filename << "screenshot_"; + filename << std::setfill('0') << std::setw(6) << (screenshotIndex++); + filename << ".png"; + + Client::Ref().WriteFile(data, filename.str()); + doScreenshot = false; + } + + if(recording) + { + VideoBuffer screenshot(ren->DumpFrame()); + std::vector<char> data = format::VideoBufferToPPM(screenshot); + + std::stringstream filename; + filename << "frame_"; + filename << std::setfill('0') << std::setw(6) << (recordingIndex++); + filename << ".ppm"; + + Client::Ref().WriteFile(data, filename.str()); + } + + int startX = 20; + int startY = YRES-20; + int startAlpha; + if(lastLogEntry>0.1f && logEntries.size()) + { + startAlpha = 2.55f*lastLogEntry; + deque<string>::iterator iter; + for(iter = logEntries.begin(); iter != logEntries.end() && startAlpha>0; iter++) + { + string message = (*iter); + startY -= 13; + g->fillrect(startX-3, startY-3, Graphics::textwidth((char*)message.c_str())+6, 14, 0, 0, 0, 100); + g->drawtext(startX, startY, message.c_str(), 255, 255, 255, startAlpha); + startAlpha-=14; + } + } + } + + if(recording) + { + std::stringstream sampleInfo; + sampleInfo << recordingIndex; + sampleInfo << ". \x8E REC"; + + int textWidth = Graphics::textwidth((char*)sampleInfo.str().c_str()); + g->fillrect(XRES-20-textWidth, 12, textWidth+8, 15, 0, 0, 0, 255*0.5); + g->drawtext(XRES-16-textWidth, 16, (const char*)sampleInfo.str().c_str(), 255, 50, 20, 255); + } + else if(showHud && !introText) + { + //Draw info about simulation under cursor + int wavelengthGfx = 0; + std::stringstream sampleInfo; + sampleInfo.precision(2); + if(sample.particle.type) + { + if(showDebug) + { + int ctype = sample.particle.ctype; + if (sample.particle.type == PT_PIPE || sample.particle.type == PT_PPIP) + ctype = sample.particle.tmp; + + if (sample.particle.type == PT_LAVA && ctype > 0 && ctype < PT_NUM) + sampleInfo << "Molten " << c->ElementResolve(ctype); + else if((sample.particle.type == PT_PIPE || sample.particle.type == PT_PPIP) && ctype > 0 && ctype < PT_NUM) + sampleInfo << c->ElementResolve(sample.particle.type) << " with " << c->ElementResolve(ctype); + else + { + sampleInfo << c->ElementResolve(sample.particle.type); + if(ctype > 0 && ctype < PT_NUM) + sampleInfo << " (" << c->ElementResolve(ctype) << ")"; + else + sampleInfo << " ()"; + } + sampleInfo << ", Temp: " << std::fixed << sample.particle.temp -273.15f; + sampleInfo << ", Life: " << sample.particle.life; + sampleInfo << ", Tmp: " << sample.particle.tmp; + sampleInfo << ", Pressure: " << std::fixed << sample.AirPressure; + } + else + { + if (sample.particle.type == PT_LAVA && sample.particle.ctype > 0 && sample.particle.ctype < PT_NUM) + sampleInfo << "Molten " << c->ElementResolve(sample.particle.ctype); + else if((sample.particle.type == PT_PIPE || sample.particle.type == PT_PPIP) && sample.particle.tmp > 0 && sample.particle.tmp < PT_NUM) + sampleInfo << c->ElementResolve(sample.particle.type) << " with " << c->ElementResolve(sample.particle.tmp); + else + sampleInfo << c->ElementResolve(sample.particle.type); + sampleInfo << ", Temp: " << std::fixed << sample.particle.temp -273.15f; + sampleInfo << ", Pressure: " << std::fixed << sample.AirPressure; + } + if(sample.particle.type == PT_PHOT) + wavelengthGfx = sample.particle.ctype; + } + else if (sample.WallType) + { + sampleInfo << c->WallName(sample.WallType); + sampleInfo << ", Pressure: " << std::fixed << sample.AirPressure; + } + else + { + sampleInfo << "Empty, Pressure: " << std::fixed << sample.AirPressure; + } + + int textWidth = Graphics::textwidth((char*)sampleInfo.str().c_str()); + g->fillrect(XRES-20-textWidth, 12, textWidth+8, 15, 0, 0, 0, 255*0.5); + g->drawtext(XRES-16-textWidth, 16, (const char*)sampleInfo.str().c_str(), 255, 255, 255, 255*0.75); + +#ifndef OGLI + if(wavelengthGfx) + { + int i, cr, cg, cb, j, h = 3, x = XRES-19-textWidth, y = 10; + int tmp; + g->fillrect(x, y, 30, h, 64, 64, 64, 255); // coords -1 size +1 to work around bug in fillrect - TODO: fix fillrect + for (i = 0; i < 30; i++) + { + if ((wavelengthGfx >> i)&1) + { + // Need a spread of wavelengths to get a smooth spectrum, 5 bits seems to work reasonably well + if (i>2) tmp = 0x1F << (i-2); + else tmp = 0x1F >> (2-i); + + cg = 0; + cb = 0; + cr = 0; + + for (j=0; j<12; j++) + { + cr += (tmp >> (j+18)) & 1; + cb += (tmp >> j) & 1; + } + for (j=0; j<13; j++) + cg += (tmp >> (j+9)) & 1; + + tmp = 624/(cr+cg+cb+1); + cr *= tmp; + cg *= tmp; + cb *= tmp; + for (j=0; j<h; j++) + g->blendpixel(x+29-i,y+j,cr>255?255:cr,cg>255?255:cg,cb>255?255:cb,255); + } + } + } +#endif + + if(showDebug) + { + sampleInfo.str(std::string()); + + if(sample.particle.type) + { + sampleInfo << "#" << sample.ParticleID << ", "; + } + sampleInfo << "X:" << sample.PositionX << " Y:" << sample.PositionY; + if (sample.Gravity) + sampleInfo << " GX: " << sample.GravityVelocityX << " GY: " << sample.GravityVelocityY; + + textWidth = Graphics::textwidth((char*)sampleInfo.str().c_str()); + g->fillrect(XRES-20-textWidth, 26, textWidth+8, 15, 0, 0, 0, 255*0.5); + g->drawtext(XRES-16-textWidth, 30, (const char*)sampleInfo.str().c_str(), 255, 255, 255, 255*0.75); + } + + + //FPS and some version info +#ifndef DEBUG //In debug mode, the Engine will draw FPS and other info instead + std::stringstream fpsInfo; + fpsInfo.precision(2); +#ifdef SNAPSHOT + fpsInfo << "Snapshot " << SNAPSHOT_ID << ", "; +#elif defined(BETA) + fpsInfo << "Beta " << SAVE_VERSION << "." << MINOR_VERSION << "." << BUILD_NUM << ", "; +#endif + fpsInfo << "FPS: " << std::fixed << ui::Engine::Ref().GetFps(); + + if (showDebug) + fpsInfo << " Parts: " << sample.NumParts; + if (ren->GetGridSize()) + fpsInfo << " [GRID: " << ren->GetGridSize() << "]"; + + textWidth = Graphics::textwidth((char*)fpsInfo.str().c_str()); + g->fillrect(12, 12, textWidth+8, 15, 0, 0, 0, 255*0.5); + g->drawtext(16, 16, (const char*)fpsInfo.str().c_str(), 32, 216, 255, 255*0.75); +#endif + } + + //Tooltips + if(infoTipPresence) + { + int infoTipAlpha = (infoTipPresence>50?50:infoTipPresence)*5; + g->drawtext((XRES-Graphics::textwidth((char*)infoTip.c_str()))/2, (YRES/2)-2, (char*)infoTip.c_str(), 255, 255, 255, infoTipAlpha); + } + + if(toolTipPresence && toolTipPosition.X!=-1 && toolTipPosition.Y!=-1 && toolTip.length()) + { + g->drawtext(toolTipPosition.X, toolTipPosition.Y, (char*)toolTip.c_str(), 255, 255, 255, toolTipPresence>51?255:toolTipPresence*5); + } + + if(buttonTipShow > 0) + { + g->drawtext(6, Size.Y-MENUSIZE-10, (char*)buttonTip.c_str(), 255, 255, 255, buttonTipShow>51?255:buttonTipShow*5); + } + + //Introduction text + if(introText) + { + g->fillrect(0, 0, XRES+BARSIZE, YRES+MENUSIZE, 0, 0, 0, introText>51?102:introText*2); + g->drawtext(16, 20, (char*)introTextMessage.c_str(), 255, 255, 255, introText>51?255:introText*5); + } +} + +ui::Point GameView::lineSnapCoords(ui::Point point1, ui::Point point2) +{ + ui::Point newPoint(0, 0); + float snapAngle = floor(atan2((float)point2.Y-point1.Y, point2.X-point1.X)/(M_PI*0.25)+0.5)*M_PI*0.25; + float lineMag = sqrtf(pow((float)(point2.X-point1.X),2)+pow((float)(point2.Y-point1.Y),2)); + newPoint.X = (int)(lineMag*cos(snapAngle)+point1.X+0.5f); + newPoint.Y = (int)(lineMag*sin(snapAngle)+point1.Y+0.5f); + return newPoint; +} + +ui::Point GameView::rectSnapCoords(ui::Point point1, ui::Point point2) +{ + ui::Point newPoint(0, 0); + float snapAngle = floor((atan2((float)point2.Y-point1.Y, point2.X-point1.X)+M_PI*0.25)/(M_PI*0.5)+0.5)*M_PI*0.5 - M_PI*0.25; + float lineMag = sqrtf(pow((float)(point2.X-point1.X),2)+pow((float)(point2.Y-point1.Y),2)); + newPoint.X = (int)(lineMag*cos(snapAngle)+point1.X+0.5f); + newPoint.Y = (int)(lineMag*sin(snapAngle)+point1.Y+0.5f); + return newPoint; +} diff --git a/src/gui/game/GameView.h b/src/gui/game/GameView.h new file mode 100644 index 0000000..093d80b --- /dev/null +++ b/src/gui/game/GameView.h @@ -0,0 +1,189 @@ +#ifndef GAMEVIEW_H +#define GAMEVIEW_H + +#include <vector> +#include <queue> +#include <deque> +#include <string> +#include "GameController.h" +#include "GameModel.h" +#include "gui/interface/Window.h" +#include "gui/interface/Point.h" +#include "gui/interface/Button.h" +#include "gui/interface/Slider.h" +#include "gui/interface/Textbox.h" +#include "ToolButton.h" +#include "Brush.h" +#include "simulation/Sample.h" + +using namespace std; + +enum DrawMode +{ + DrawPoints, DrawLine, DrawRect, DrawFill +}; + +enum SelectMode +{ + SelectNone, SelectStamp, SelectCopy, SelectCut, PlaceSave +}; + +class GameController; +class GameModel; +class GameView: public ui::Window +{ +private: + DrawMode drawMode; + + bool doScreenshot; + bool recording; + int screenshotIndex; + int recordingIndex; + + bool isMouseDown; + bool zoomEnabled; + bool zoomCursorFixed; + bool drawSnap; + bool shiftBehaviour; + bool ctrlBehaviour; + bool altBehaviour; + bool showHud; + bool showDebug; + bool wallBrush; + int introText; + int buttonTipShow; + std::string buttonTip; + std::string introTextMessage; + int toolIndex; + int currentSaveType; + Menu * lastMenu; + + int infoTipPresence; + std::string toolTip; + ui::Point toolTipPosition; + std::string infoTip; + int toolTipPresence; + + queue<ui::Point> pointQueue; + GameController * c; + Renderer * ren; + Brush * activeBrush; + //UI Elements + vector<ui::Button*> quickOptionButtons; + vector<ui::Button*> menuButtons; + vector<ToolButton*> toolButtons; + vector<ui::Component*> notificationComponents; + deque<string> logEntries; + float lastLogEntry; + ui::Button * scrollBar; + ui::Button * searchButton; + ui::Button * reloadButton; + ui::Button * saveSimulationButton; + ui::Button * downVoteButton; + ui::Button * upVoteButton; + ui::Button * tagSimulationButton; + ui::Button * clearSimButton; + ui::Button * loginButton; + ui::Button * simulationOptionButton; + ui::Button * displayModeButton; + ui::Button * pauseButton; + ui::Point currentMouse; + + ui::Button * colourPicker; + vector<ToolButton*> colourPresets; + + bool drawModeReset; + ui::Point drawPoint1; + ui::Point drawPoint2; + + SelectMode selectMode; + ui::Point selectPoint1; + ui::Point selectPoint2; + + ui::Point mousePosition; + + VideoBuffer * placeSaveThumb; + + SimulationSample sample; + + int lastOffset; + void setToolButtonOffset(int offset); + virtual ui::Point lineSnapCoords(ui::Point point1, ui::Point point2); + virtual ui::Point rectSnapCoords(ui::Point point1, ui::Point point2); + + void screenshot(); + void record(); + + void enableShiftBehaviour(); + void disableShiftBehaviour(); + void enableCtrlBehaviour(); + void disableCtrlBehaviour(); + void enableAltBehaviour(); + void disableAltBehaviour(); +public: + GameView(); + virtual ~GameView(); + + //Breaks MVC, but any other way is going to be more of a mess. + ui::Point GetMousePosition(); + void SetSample(SimulationSample sample); + void SetHudEnable(bool hudState); + bool CtrlBehaviour(){ return ctrlBehaviour; } + bool ShiftBehaviour(){ return shiftBehaviour; } + void ExitPrompt(); + void ToggleDebug(); + SelectMode GetSelectMode() { return selectMode; } + void BeginStampSelection(); + + void AttachController(GameController * _c){ c = _c; } + void NotifyRendererChanged(GameModel * sender); + void NotifySimulationChanged(GameModel * sender); + void NotifyPausedChanged(GameModel * sender); + void NotifySaveChanged(GameModel * sender); + void NotifyBrushChanged(GameModel * sender); + void NotifyMenuListChanged(GameModel * sender); + void NotifyToolListChanged(GameModel * sender); + void NotifyActiveToolsChanged(GameModel * sender); + void NotifyUserChanged(GameModel * sender); + void NotifyZoomChanged(GameModel * sender); + void NotifyColourSelectorVisibilityChanged(GameModel * sender); + void NotifyColourSelectorColourChanged(GameModel * sender); + void NotifyColourPresetsChanged(GameModel * sender); + void NotifyColourActivePresetChanged(GameModel * sender); + void NotifyPlaceSaveChanged(GameModel * sender); + void NotifyNotificationsChanged(GameModel * sender); + void NotifyLogChanged(GameModel * sender, string entry); + void NotifyToolTipChanged(GameModel * sender); + void NotifyInfoTipChanged(GameModel * sender); + void NotifyQuickOptionsChanged(GameModel * sender); + void NotifyLastToolChanged(GameModel * sender); + + + virtual void ToolTip(ui::Component * sender, ui::Point mousePosition, std::string toolTip); + + virtual void OnMouseMove(int x, int y, int dx, int dy); + virtual void OnMouseDown(int x, int y, unsigned button); + virtual void OnMouseUp(int x, int y, unsigned button); + virtual void OnMouseWheel(int x, int y, int d); + virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + virtual void OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt); + virtual void OnTick(float dt); + virtual void OnDraw(); + virtual void OnBlur(); + + //Top-level handlers, for Lua interface + virtual void DoDraw(); + virtual void DoMouseMove(int x, int y, int dx, int dy); + virtual void DoMouseDown(int x, int y, unsigned button); + virtual void DoMouseUp(int x, int y, unsigned button); + virtual void DoMouseWheel(int x, int y, int d); + virtual void DoKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + virtual void DoKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt); + + class MenuAction; + class ToolAction; + class OptionAction; + class OptionListener; +}; + +#endif // GAMEVIEW_H diff --git a/src/gui/game/Menu.h b/src/gui/game/Menu.h new file mode 100644 index 0000000..3f955e1 --- /dev/null +++ b/src/gui/game/Menu.h @@ -0,0 +1,51 @@ +#ifndef MENU_H_ +#define MENU_H_ + +#include "Tool.h" + +class Menu +{ + char icon; + string description; + vector<Tool*> tools; +public: + Menu(char icon_, string description_): + icon(icon_), + description(description_), + tools(vector<Tool*>()) + { + + } + + virtual ~Menu() + { + for(int i = 0; i < tools.size(); i++) + { + delete tools[i]; + } + tools.clear(); + } + + vector<Tool*> GetToolList() + { + return tools; + } + + char GetIcon() + { + return icon; + } + + string GetDescription() + { + return description; + } + + void AddTool(Tool * tool_) + { + tools.push_back(tool_); + } +}; + + +#endif /* MENU_H_ */ diff --git a/src/gui/game/Notification.h b/src/gui/game/Notification.h new file mode 100644 index 0000000..b2bf6f8 --- /dev/null +++ b/src/gui/game/Notification.h @@ -0,0 +1,16 @@ +#ifndef NOTIFICATION_H_ +#define NOTIFICATION_H_ + +#include <string> + +class Notification +{ +public: + Notification(std::string message) : Message(message) {} + virtual ~Notification() {}; + std::string Message; + + virtual void Action() { } +}; + +#endif /* NOTIFICATION_H_ */ diff --git a/src/gui/game/PropertyTool.cpp b/src/gui/game/PropertyTool.cpp new file mode 100644 index 0000000..05726d3 --- /dev/null +++ b/src/gui/game/PropertyTool.cpp @@ -0,0 +1,235 @@ +#include <iostream> +#include <sstream> +#include "gui/Style.h" +#include "simulation/Simulation.h" +#include "Tool.h" +#include "gui/interface/Window.h" +#include "gui/interface/Button.h" +#include "gui/interface/Label.h" +#include "gui/interface/Textbox.h" +#include "gui/interface/DropDown.h" +#include "gui/interface/Keys.h" +#include "gui/dialogues/ErrorMessage.h" + +class PropertyWindow: public ui::Window +{ +public: + ui::DropDown * property; + ui::Textbox * textField; + SignTool * tool; + Simulation * sim; + int signID; + ui::Point position; + std::vector<StructProperty> properties; + PropertyWindow(PropertyTool * tool_, Simulation * sim_, ui::Point position_); + void SetProperty(); + virtual void OnDraw(); + virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + virtual void OnTryExit(ExitMethod method); + virtual ~PropertyWindow() {} + class OkayAction: public ui::ButtonAction + { + public: + PropertyWindow * prompt; + OkayAction(PropertyWindow * prompt_) { prompt = prompt_; } + void ActionCallback(ui::Button * sender) + { + ui::Engine::Ref().CloseWindow(); + if(prompt->textField->GetText().length()) + prompt->SetProperty(); + prompt->SelfDestruct(); + return; + } + }; +}; + +PropertyWindow::PropertyWindow(PropertyTool * tool_, Simulation * sim_, ui::Point position_): +ui::Window(ui::Point(-1, -1), ui::Point(200, 87)), +sim(sim_), +position(position_) +{ + properties = Particle::GetProperties(); + + ui::Label * messageLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 14), "Edit property"); + messageLabel->SetTextColour(style::Colour::InformationTitle); + messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop; + AddComponent(messageLabel); + + ui::Button * okayButton = new ui::Button(ui::Point(0, Size.Y-17), ui::Point(Size.X, 17), "OK"); + okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + okayButton->Appearance.BorderInactive = ui::Colour(200, 200, 200); + okayButton->SetActionCallback(new OkayAction(this)); + AddComponent(okayButton); + SetOkayButton(okayButton); + + class PropertyChanged: public ui::DropDownAction + { + PropertyWindow * w; + public: + PropertyChanged(PropertyWindow * w): w(w) { } + virtual void OptionChanged(ui::DropDown * sender, std::pair<std::string, int> option) + { + w->FocusComponent(w->textField); + } + }; + property = new ui::DropDown(ui::Point(8, 25), ui::Point(Size.X-16, 17)); + property->SetActionCallback(new PropertyChanged(this)); + AddComponent(property); + for(int i = 0; i < properties.size(); i++) + { + property->AddOption(std::pair<std::string, int>(properties[i].Name, i)); + } + property->SetOption(0); + + textField = new ui::Textbox(ui::Point(8, 46), ui::Point(Size.X-16, 16), "", "[value]"); + textField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + textField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(textField); + FocusComponent(textField); + + ui::Engine::Ref().ShowWindow(this); +} +void PropertyWindow::SetProperty() +{ + if(property->GetOption().second!=-1 && textField->GetText().length() > 0) + { + void * propValue; + int tempInt; + unsigned int tempUInt; + float tempFloat; + std::string value = textField->GetText(); + try { + switch(properties[property->GetOption().second].Type) + { + case StructProperty::Integer: + case StructProperty::ParticleType: + if(value.length() > 2 && value.substr(0, 2) == "0x") + { + //0xC0FFEE + std::stringstream buffer; + buffer.exceptions(std::stringstream::failbit | std::stringstream::badbit); + buffer << std::hex << value.substr(2); + buffer >> tempInt; + } + else if(value.length() > 1 && value[0] == '#') + { + //#C0FFEE + std::stringstream buffer; + buffer.exceptions(std::stringstream::failbit | std::stringstream::badbit); + buffer << std::hex << value.substr(1); + buffer >> tempInt; + } + else + { + if(properties[property->GetOption().second].Type == StructProperty::ParticleType) + { + int type = sim->GetParticleType(value); + if(type != -1) + { +#ifdef DEBUG + std::cout << "Got type from particle name" << std::endl; +#endif + tempInt = type; + } + else + { + std::stringstream buffer(value); + buffer.exceptions(std::stringstream::failbit | std::stringstream::badbit); + buffer >> tempInt; + } + } + else + { + std::stringstream buffer(value); + buffer.exceptions(std::stringstream::failbit | std::stringstream::badbit); + buffer >> tempInt; + } + } +#ifdef DEBUG + std::cout << "Got int value " << tempInt << std::endl; +#endif + propValue = &tempInt; + break; + case StructProperty::UInteger: + if(value.length() > 2 && value.substr(0, 2) == "0x") + { + //0xC0FFEE + std::stringstream buffer; + buffer.exceptions(std::stringstream::failbit | std::stringstream::badbit); + buffer << std::hex << value.substr(2); + buffer >> tempUInt; + } + else if(value.length() > 1 && value[0] == '#') + { + //#C0FFEE + std::stringstream buffer; + buffer.exceptions(std::stringstream::failbit | std::stringstream::badbit); + buffer << std::hex << value.substr(1); + buffer >> tempUInt; + } + else + { + std::stringstream buffer(value); + buffer.exceptions(std::stringstream::failbit | std::stringstream::badbit); + buffer >> tempUInt; + } +#ifdef DEBUG + std::cout << "Got uint value " << tempUInt << std::endl; +#endif + propValue = &tempUInt; + break; + case StructProperty::Float: + { + std::stringstream buffer(value); + buffer.exceptions(std::stringstream::failbit | std::stringstream::badbit); + buffer >> tempFloat; +#ifdef DEBUG + std::cout << "Got float value " << tempFloat << std::endl; +#endif + propValue = &tempFloat; + } + break; + default: + new ErrorMessage("Could not set property", "Invalid property"); + } + sim->flood_prop( + position.X, + position.Y, + properties[property->GetOption().second].Offset, + propValue, + properties[property->GetOption().second].Type + ); + } catch (const std::exception& ex) { + new ErrorMessage("Could not set property", "Invalid value provided"); + } + } +} + +void PropertyWindow::OnTryExit(ExitMethod method) +{ + ui::Engine::Ref().CloseWindow(); + SelfDestruct(); +} + +void PropertyWindow::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + + g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255); +} + +void PropertyWindow::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + if (key == KEY_UP) + property->SetOption(property->GetOption().second-1); + else if (key == KEY_DOWN) + property->SetOption(property->GetOption().second+1); +} + +void PropertyTool::Click(Simulation * sim, Brush * brush, ui::Point position) +{ + new PropertyWindow(this, sim, position); +}
\ No newline at end of file diff --git a/src/gui/game/QuickOption.h b/src/gui/game/QuickOption.h new file mode 100644 index 0000000..4bf27ff --- /dev/null +++ b/src/gui/game/QuickOption.h @@ -0,0 +1,76 @@ +#pragma once + +#include <string> +#include <vector> + +class GameModel; +class QuickOption; +class QuickOptionListener +{ +protected: + QuickOptionListener() {} +public: + virtual ~QuickOptionListener() {} + virtual void OnValueChanged(QuickOption * sender) {} +}; +class QuickOption +{ +public: + enum Type { + Toggle, Multi + }; +protected: + std::vector<QuickOptionListener*> listeners; + GameModel * m; + Type type; + std::string icon; + std::string description; + QuickOption(std::string icon, std::string description, GameModel * m, Type type) : + icon(icon), + description(description), + m(m), + type(type) + { + + } + virtual void perform() {} +public: + virtual ~QuickOption() + { + //for(std::vector<QuickOptionListener*>::iterator iter = listeners.begin(), end = listeners.end(); iter != end; ++iter) + // delete *iter; + } + + std::vector<QuickOptionListener*> GetListeners() + { + return listeners; + } + + void AddListener(QuickOptionListener * listener) + { + listeners.push_back(listener); + } + + Type GetType() { return type; } + + virtual bool GetToggle() { return true;} + virtual int GetMutli() { return 0;} + virtual int GetMultiCount() { return 0;} + + std::string GetIcon() { return icon; } + void SetIcon(std::string icon) { this->icon = icon; } + std::string GetDescription() { return description; } + void SetDescription(std::string description) { this->description = description; } + void Perform() + { + perform(); + for(std::vector<QuickOptionListener*>::iterator iter = listeners.begin(), end = listeners.end(); iter != end; ++iter) + (*iter)->OnValueChanged(this); + } + void Update() + { + for(std::vector<QuickOptionListener*>::iterator iter = listeners.begin(), end = listeners.end(); iter != end; ++iter) + (*iter)->OnValueChanged(this); + } +}; + diff --git a/src/gui/game/QuickOptions.h b/src/gui/game/QuickOptions.h new file mode 100644 index 0000000..7c31f9c --- /dev/null +++ b/src/gui/game/QuickOptions.h @@ -0,0 +1,120 @@ +#include "QuickOption.h" +#include "GameModel.h" + +class SandEffectOption: public QuickOption +{ +public: + SandEffectOption(GameModel * m): + QuickOption("P", "Sand effect", m, Toggle) + { + + } + virtual bool GetToggle() + { + return m->GetSimulation()->pretty_powder; + } + virtual void perform() + { + m->GetSimulation()->pretty_powder = !m->GetSimulation()->pretty_powder; + } +}; + +class DrawGravOption: public QuickOption +{ +public: + DrawGravOption(GameModel * m): + QuickOption("G", "Draw gravity field", m, Toggle) + { + + } + virtual bool GetToggle() + { + return m->GetGravityGrid(); + } + virtual void perform() + { + m->ShowGravityGrid(!m->GetGravityGrid()); + } +}; + +class DecorationsOption: public QuickOption +{ +public: + DecorationsOption(GameModel * m): + QuickOption("D", "Draw decorations", m, Toggle) + { + + } + virtual bool GetToggle() + { + return m->GetDecoration(); + } + virtual void perform() + { + m->SetDecoration(!m->GetDecoration()); + } +}; + +class NGravityOption: public QuickOption +{ +public: + NGravityOption(GameModel * m): + QuickOption("N", "Newtonian Gravity", m, Toggle) + { + + } + virtual bool GetToggle() + { + return m->GetSimulation()->grav->ngrav_enable; + } + virtual void perform() + { + if(m->GetSimulation()->grav->ngrav_enable) + { + m->GetSimulation()->grav->stop_grav_async(); + m->SetInfoTip("Newtonian Gravity: Off"); + } + else + { + m->GetSimulation()->grav->start_grav_async(); + m->SetInfoTip("Newtonian Gravity: On"); + } + } +}; + +class AHeatOption: public QuickOption +{ +public: + AHeatOption(GameModel * m): + QuickOption("A", "Ambient heat", m, Toggle) + { + + } + virtual bool GetToggle() + { + return m->GetAHeatEnable(); + } + virtual void perform() + { + m->SetAHeatEnable(!m->GetAHeatEnable()); + } +}; + +class ConsoleShowOption: public QuickOption +{ + GameController * c; +public: + ConsoleShowOption(GameModel * m, GameController * c_): + QuickOption("C", "Show Console", m, Toggle) + { + c = c_; + } + virtual bool GetToggle() + { + return 0; + } + virtual void perform() + { + c->ShowConsole(); + } +}; diff --git a/src/gui/game/RenderPreset.h b/src/gui/game/RenderPreset.h new file mode 100644 index 0000000..9cc9f4c --- /dev/null +++ b/src/gui/game/RenderPreset.h @@ -0,0 +1,19 @@ +#ifndef RENDER_PRESET_H +#define RENDER_PRESET_H +class RenderPreset +{ +public: + std::string Name; + std::vector<unsigned int> RenderModes; + std::vector<unsigned int> DisplayModes; + unsigned int ColourMode; + + RenderPreset(): Name(""), ColourMode(0) {} + RenderPreset(std::string name, std::vector<unsigned int> renderModes, std::vector<unsigned int> displayModes, unsigned int colourMode): + Name(name), + RenderModes(renderModes), + DisplayModes(displayModes), + ColourMode(colourMode) + {} +}; +#endif
\ No newline at end of file diff --git a/src/gui/game/SampleTool.cpp b/src/gui/game/SampleTool.cpp new file mode 100644 index 0000000..b914b1c --- /dev/null +++ b/src/gui/game/SampleTool.cpp @@ -0,0 +1,67 @@ +#include <iostream> +#include "graphics/Graphics.h" +#include "Tool.h" +#include "GameModel.h" +#include "gui/interface/Colour.h" + +VideoBuffer * SampleTool::GetIcon(int toolID, int width, int height) +{ + VideoBuffer * newTexture = new VideoBuffer(width, height); + for (int y=0; y<height; y++) + { + for (int x=0; x<width; x++) + { + pixel pc = x==0||x==width-1||y==0||y==height-1 ? PIXPACK(0xA0A0A0) : PIXPACK(0x000000); + newTexture->SetPixel(x, y, PIXR(pc), PIXG(pc), PIXB(pc), 255); + } + } + newTexture->SetCharacter((width/2)-5, (height/2)-5, 0xE6, 255, 255, 255, 255); + newTexture->BlendCharacter((width/2)-5, (height/2)-5, 0xE7, 100, 180, 255, 255); + return newTexture; +} + +void SampleTool::Draw(Simulation * sim, Brush * brush, ui::Point position) +{ + if(gameModel->GetColourSelectorVisibility()) + { + pixel colour = gameModel->GetRenderer()->sampleColor; + gameModel->SetColourSelectorColour(ui::Colour(PIXR(colour), PIXG(colour), PIXB(colour), 255)); + } + else + { + int particleType = 0; + int particleCtype = 0; + if (sim->photons[position.Y][position.X]) + { + particleType = sim->parts[sim->photons[position.Y][position.X]>>8].type; + particleCtype = sim->parts[sim->pmap[position.Y][position.X]>>8].ctype; + } + else if (sim->pmap[position.Y][position.X]) + { + particleType = sim->parts[sim->pmap[position.Y][position.X]>>8].type; + particleCtype = sim->parts[sim->pmap[position.Y][position.X]>>8].ctype; + } + + if(particleType) + { + if(particleType == PT_LIFE) + { + Menu * lifeMenu = gameModel->GetMenuList()[SC_LIFE]; + std::vector<Tool*> elementTools = lifeMenu->GetToolList(); + + for(std::vector<Tool*>::iterator iter = elementTools.begin(), end = elementTools.end(); iter != end; ++iter) + { + Tool * elementTool = *iter; + if(elementTool && elementTool->GetToolID() == particleCtype) + gameModel->SetActiveTool(0, elementTool); + } + } + else + { + Tool * elementTool = gameModel->GetElementTool(particleType); + if(elementTool) + gameModel->SetActiveTool(0, elementTool); + } + } + } +} diff --git a/src/gui/game/SignTool.cpp b/src/gui/game/SignTool.cpp new file mode 100644 index 0000000..a4042b1 --- /dev/null +++ b/src/gui/game/SignTool.cpp @@ -0,0 +1,278 @@ +#include <iostream> +#include "gui/Style.h" +#include "simulation/Simulation.h" +#include "Tool.h" +#include "gui/interface/Window.h" +#include "gui/interface/Button.h" +#include "gui/interface/Label.h" +#include "gui/interface/Textbox.h" +#include "gui/interface/DropDown.h" + +class SignWindow: public ui::Window +{ +public: + ui::DropDown * justification; + ui::Textbox * textField; + SignTool * tool; + sign * movingSign; + bool signMoving; + Simulation * sim; + int signID; + ui::Point signPosition; + SignWindow(SignTool * tool_, Simulation * sim_, int signID_, ui::Point position_); + virtual void OnDraw(); + virtual void DoDraw(); + virtual void DoMouseMove(int x, int y, int dx, int dy); + virtual void DoMouseDown(int x, int y, unsigned button); + virtual void DoMouseUp(int x, int y, unsigned button) { if(!signMoving) ui::Window::DoMouseUp(x, y, button); } + virtual void DoMouseWheel(int x, int y, int d) { if(!signMoving) ui::Window::DoMouseWheel(x, y, d); } + virtual void DoKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) { if(!signMoving) ui::Window::DoKeyPress(key, character, shift, ctrl, alt); }; + virtual void DoKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt) { if(!signMoving) ui::Window::DoKeyRelease(key, character, shift, ctrl, alt); }; + virtual ~SignWindow() {} + virtual void OnTryExit(ui::Window::ExitMethod method); + class OkayAction: public ui::ButtonAction + { + public: + SignWindow * prompt; + OkayAction(SignWindow * prompt_) { prompt = prompt_; } + void ActionCallback(ui::Button * sender) + { + ui::Engine::Ref().CloseWindow(); + if(prompt->signID==-1 && prompt->textField->GetText().length()) + { + prompt->sim->signs.push_back(sign(prompt->textField->GetText(), prompt->signPosition.X, prompt->signPosition.Y, (sign::Justification)prompt->justification->GetOption().second)); + } + else if(prompt->signID!=-1 && prompt->textField->GetText().length()) + { + prompt->sim->signs[prompt->signID] = sign(sign(prompt->textField->GetText(), prompt->signPosition.X, prompt->signPosition.Y, (sign::Justification)prompt->justification->GetOption().second)); + } + prompt->SelfDestruct(); + } + }; + class DeleteAction: public ui::ButtonAction + { + public: + SignWindow * prompt; + DeleteAction(SignWindow * prompt_) { prompt = prompt_; } + void ActionCallback(ui::Button * sender) + { + ui::Engine::Ref().CloseWindow(); + if(prompt->signID!=-1) + { + prompt->sim->signs.erase(prompt->sim->signs.begin()+prompt->signID); + } + prompt->SelfDestruct(); + } + }; + + class SignTextAction: public ui::TextboxAction + { + public: + SignWindow * prompt; + SignTextAction(SignWindow * prompt_) { prompt = prompt_; } + virtual void TextChangedCallback(ui::Textbox * sender) + { + if(prompt->signID!=-1) + { + prompt->sim->signs[prompt->signID].text = sender->GetText(); + prompt->sim->signs[prompt->signID].ju = (sign::Justification)prompt->justification->GetOption().second; + } + } + }; + + class MoveAction: public ui::ButtonAction + { + public: + SignWindow * prompt; + MoveAction(SignWindow * prompt_) { prompt = prompt_; } + void ActionCallback(ui::Button * sender) + { + if(prompt->signID!=-1) + { + prompt->movingSign = &prompt->sim->signs[prompt->signID]; + prompt->sim->signs[prompt->signID].ju = (sign::Justification)prompt->justification->GetOption().second; + prompt->signMoving = true; + } + } + }; +}; + +SignWindow::SignWindow(SignTool * tool_, Simulation * sim_, int signID_, ui::Point position_): + ui::Window(ui::Point(-1, -1), ui::Point(200, 87)), + tool(tool_), + signID(signID_), + sim(sim_), + signPosition(position_), + movingSign(NULL), + signMoving(false) +{ + ui::Label * messageLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 15), "New sign"); + messageLabel->SetTextColour(style::Colour::InformationTitle); + messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(messageLabel); + + ui::Button * okayButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "OK"); + okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + okayButton->Appearance.BorderInactive = (ui::Colour(200, 200, 200)); + okayButton->SetActionCallback(new OkayAction(this)); + AddComponent(okayButton); + SetOkayButton(okayButton); + + ui::Label * tempLabel = new ui::Label(ui::Point(8, 48), ui::Point(40, 15), "Justify:"); + okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(tempLabel); + + justification = new ui::DropDown(ui::Point(52, 48), ui::Point(50, 16)); + AddComponent(justification); + justification->AddOption(std::pair<std::string, int>("\x9D Left", (int)sign::Left)); + justification->AddOption(std::pair<std::string, int>("\x9E Centre", (int)sign::Centre)); + justification->AddOption(std::pair<std::string, int>("\x9F Right", (int)sign::Right)); + justification->SetOption(1); + justification->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + + textField = new ui::Textbox(ui::Point(8, 25), ui::Point(Size.X-16, 17), "", "[message]"); + textField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + textField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + textField->SetActionCallback(new SignTextAction(this)); + AddComponent(textField); + FocusComponent(textField); + + if(signID!=-1) + { + messageLabel->SetText("Edit sign"); + + textField->SetText(sim->signs[signID].text); + justification->SetOption(sim->signs[signID].ju); + + ui::Point position = ui::Point(justification->Position.X+justification->Size.X+3, 48); + ui::Button * moveButton = new ui::Button(position, ui::Point(((Size.X-position.X-8)/2)-2, 16), "Move"); + moveButton->SetActionCallback(new MoveAction(this)); + AddComponent(moveButton); + + position = ui::Point(justification->Position.X+justification->Size.X+3, 48)+ui::Point(moveButton->Size.X+3, 0); + ui::Button * deleteButton = new ui::Button(position, ui::Point((Size.X-position.X-8)-1, 16), "Delete"); + //deleteButton->SetIcon(IconDelete); + deleteButton->SetActionCallback(new DeleteAction(this)); + + signPosition.X = sim->signs[signID].x; + signPosition.Y = sim->signs[signID].y; + + AddComponent(deleteButton); + } + + ui::Engine::Ref().ShowWindow(this); +} + +void SignWindow::OnTryExit(ui::Window::ExitMethod method) +{ + ui::Engine::Ref().CloseWindow(); + SelfDestruct(); +} + +void SignWindow::DoDraw() +{ + for(std::vector<sign>::iterator iter = sim->signs.begin(), end = sim->signs.end(); iter != end; ++iter) + { + sign & currentSign = *iter; + int x, y, w, h, dx, dy; + Graphics * g = ui::Engine::Ref().g; + std::string text = currentSign.getText(sim); + currentSign.pos(text, x, y, w, h); + g->clearrect(x, y, w+1, h); + g->drawrect(x, y, w+1, h, 192, 192, 192, 255); + if (sregexp(currentSign.text.c_str(), "^{[c|t]:[0-9]*|.*}$")) + g->drawtext(x+3, y+3, text, 255, 255, 255, 255); + else + g->drawtext(x+3, y+3, text, 0, 191, 255, 255); + + x = currentSign.x; + y = currentSign.y; + dx = 1 - currentSign.ju; + dy = (currentSign.y > 18) ? -1 : 1; +#ifdef OGLR + glBegin(GL_LINES); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glVertex2i(x, y); + glVertex2i(x+(dx*4), y+(dy*4)); + glEnd(); +#else + for (int j=0; j<4; j++) + { + g->blendpixel(x, y, 192, 192, 192, 255); + x+=dx; + y+=dy; + } +#endif + } + if(!signMoving) + { + ui::Window::DoDraw(); + } +} + +void SignWindow::DoMouseMove(int x, int y, int dx, int dy) { + if(!signMoving) + ui::Window::DoMouseMove(x, y, dx, dy); + else + { + if(x < XRES && y < YRES) + { + movingSign->x = x; + movingSign->y = y; + signPosition.X = x; + signPosition.Y = y; + } + } +} + +void SignWindow::DoMouseDown(int x, int y, unsigned button) +{ + if(!signMoving) + ui::Window::DoMouseDown(x, y, button); + else + { + signMoving = false; + } +} + +void SignWindow::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + + g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255); +} + +VideoBuffer * SignTool::GetIcon(int toolID, int width, int height) +{ + VideoBuffer * newTexture = new VideoBuffer(width, height); + for (int y=0; y<height; y++) + { + for (int x=0; x<width; x++) + { + pixel pc = x==0||x==width-1||y==0||y==height-1 ? PIXPACK(0xA0A0A0) : PIXPACK(0x000000); + newTexture->SetPixel(x, y, PIXR(pc), PIXG(pc), PIXB(pc), 255); + } + } + newTexture->SetCharacter((width/2)-5, (height/2)-5, 0xA1, 32, 64, 128, 255); + newTexture->BlendCharacter((width/2)-5, (height/2)-5, 0xA0, 255, 255, 255, 255); + return newTexture; +} + +void SignTool::Click(Simulation * sim, Brush * brush, ui::Point position) +{ + int signX, signY, signW, signH, signIndex = -1; + for(int i = 0; i < sim->signs.size(); i++){ + sim->signs[i].pos(sim->signs[i].getText(sim), signX, signY, signW, signH); + if(position.X > signX && position.X < signX+signW && position.Y > signY && position.Y < signY+signH) + { + signIndex = i; + break; + } + } + if (signIndex != -1 || sim->signs.size() < MAXSIGNS) + new SignWindow(this, sim, signIndex, position); +} diff --git a/src/gui/game/Tool.cpp b/src/gui/game/Tool.cpp new file mode 100644 index 0000000..def6e46 --- /dev/null +++ b/src/gui/game/Tool.cpp @@ -0,0 +1,215 @@ +#include <string> +#include "Tool.h" +#include "gui/game/Brush.h" + +#include "simulation/Simulation.h" + +using namespace std; + +Tool::Tool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int)): + toolID(id), + toolName(name), + toolDescription(description), + colRed(r), + colGreen(g), + colBlue(b), + textureGen(textureGen), + strength(1.0f), + resolution(1), + identifier(identifier) +{ +} + +VideoBuffer * Tool::GetTexture(int width, int height) +{ + if(textureGen) + { + return textureGen(toolID, width, height); + } + return NULL; +} +void Tool::SetTextureGen(VideoBuffer * (*textureGen)(int, int, int)) +{ + this->textureGen = textureGen; +} +std::string Tool::GetIdentifier() { return identifier; } +string Tool::GetName() { return toolName; } +string Tool::GetDescription() { return toolDescription; } +Tool::~Tool() {} + +void Tool::Click(Simulation * sim, Brush * brush, ui::Point position) { } +void Tool::Draw(Simulation * sim, Brush * brush, ui::Point position) { + sim->ToolBrush(position.X, position.Y, toolID, brush, strength); +} +void Tool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) { + sim->ToolLine(position1.X, position1.Y, position2.X, position2.Y, toolID, brush, strength); +} +void Tool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { + sim->ToolBox(position1.X, position1.Y, position2.X, position2.Y, toolID, brush, strength); +} +void Tool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) {}; + + +ElementTool::ElementTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int)): + Tool(id, name, description, r, g, b, identifier, textureGen) +{ +} +ElementTool::~ElementTool() {} +void ElementTool::Draw(Simulation * sim, Brush * brush, ui::Point position){ + sim->CreateParts(position.X, position.Y, toolID, brush); +} +void ElementTool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) { + sim->CreateLine(position1.X, position1.Y, position2.X, position2.Y, toolID, brush); +} +void ElementTool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { + sim->CreateBox(position1.X, position1.Y, position2.X, position2.Y, toolID, 0); +} +void ElementTool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) { + sim->FloodParts(position.X, position.Y, toolID, -1, -1, 0); +} + + +WallTool::WallTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int)): +Tool(id, name, description, r, g, b, identifier, textureGen) +{ + resolution = CELL; +} +WallTool::~WallTool() {} +void WallTool::Draw(Simulation * sim, Brush * brush, ui::Point position){ + sim->CreateWalls(position.X, position.Y, 1, 1, toolID, 0, brush); +} +void WallTool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) { + int wallX = position1.X/CELL; + int wallY = position1.Y/CELL; + if(dragging == false && toolID == WL_FAN && sim->bmap[wallY][wallX]==WL_FAN) + { + float newFanVelX = (position2.X-position1.X)*0.005f; + newFanVelX *= strength; + float newFanVelY = (position2.Y-position1.Y)*0.005f; + newFanVelY *= strength; + sim->FloodWalls(position1.X, position1.Y, WL_FLOODHELPER, -1, WL_FAN, 0); + for (int j = 0; j < YRES/CELL; j++) + for (int i = 0; i < XRES/CELL; i++) + if (sim->bmap[j][i] == WL_FLOODHELPER) + { + sim->fvx[j][i] = newFanVelX; + sim->fvy[j][i] = newFanVelY; + sim->bmap[j][i] = WL_FAN; + } + } + else + { + sim->CreateWallLine(position1.X, position1.Y, position2.X, position2.Y, 1, 1, toolID, 0, brush); + } +} +void WallTool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { + sim->CreateWallBox(position1.X, position1.Y, position2.X, position2.Y, toolID, 0); +} +void WallTool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) { + if (toolID != WL_STREAM) + sim->FloodWalls(position.X, position.Y, toolID, -1, -1, 0); +} + + +GolTool::GolTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int)): + Tool(id, name, description, r, g, b, identifier, textureGen) +{ +} +GolTool::~GolTool() {} +void GolTool::Draw(Simulation * sim, Brush * brush, ui::Point position){ + sim->CreateParts(position.X, position.Y, PT_LIFE|(toolID<<8), brush); +} +void GolTool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) { + sim->CreateLine(position1.X, position1.Y, position2.X, position2.Y, PT_LIFE|(toolID<<8), brush); +} +void GolTool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { + sim->CreateBox(position1.X, position1.Y, position2.X, position2.Y, PT_LIFE|(toolID<<8), 0); +} +void GolTool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) { + sim->FloodParts(position.X, position.Y, PT_LIFE|(toolID<<8), -1, -1, 0); +} + + +WindTool::WindTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int)): + Tool(id, name, description, r, g, b, identifier, textureGen) +{ +} +WindTool::~WindTool() {} +void WindTool::Draw(Simulation * sim, Brush * brush, ui::Point position) +{ + +} +void WindTool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) +{ + int radiusX, radiusY, sizeX, sizeY; + + float strength = dragging?0.01f:0.002f; + strength *= this->strength; + + radiusX = brush->GetRadius().X; + radiusY = brush->GetRadius().Y; + + sizeX = brush->GetSize().X; + sizeY = brush->GetSize().Y; + + unsigned char *bitmap = brush->GetBitmap(); + + for(int y = 0; y < sizeY; y++) + { + for(int x = 0; x < sizeX; x++) + { + if(bitmap[(y*sizeX)+x] && (position1.X+(x-radiusX) >= 0 && position1.Y+(y-radiusY) >= 0 && position1.X+(x-radiusX) < XRES && position1.Y+(y-radiusY) < YRES)) + { + sim->vx[(position1.Y+(y-radiusY))/CELL][(position1.X+(x-radiusX))/CELL] += (position2.X-position1.X)*strength; + sim->vy[(position1.Y+(y-radiusY))/CELL][(position1.X+(x-radiusX))/CELL] += (position2.Y-position1.Y)*strength; + } + } + } +} +void WindTool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) {} +void WindTool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) {} + + +void Element_LIGH_Tool::Draw(Simulation * sim, Brush * brush, ui::Point position) +{ + if(sim->currentTick >= nextUse) + { + int p = sim->create_part(-2, position.X, position.Y, toolID); + if (p != -1) + { + sim->parts[p].life = brush->GetRadius().X+brush->GetRadius().Y; + if (sim->parts[p].life > 55) + sim->parts[p].life = 55; + sim->parts[p].temp = sim->parts[p].life*150; // temperature of the lighting shows the power of the lighting + nextUse = sim->currentTick+sim->parts[p].life/4; + } + } +} + + +Element_TESC_Tool::Element_TESC_Tool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int)): + ElementTool(id, name, description, r, g, b, identifier, textureGen) + { + } +void Element_TESC_Tool::Draw(Simulation * sim, Brush * brush, ui::Point position){ + int radiusInfo = brush->GetRadius().X*4+brush->GetRadius().Y*4+7; + sim->CreateParts(position.X, position.Y, toolID | (radiusInfo << 8), brush); +} +void Element_TESC_Tool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) { + int radiusInfo = brush->GetRadius().X*4+brush->GetRadius().Y*4+7; + sim->CreateLine(position1.X, position1.Y, position2.X, position2.Y, toolID | (radiusInfo << 8), brush); +} +void Element_TESC_Tool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { + int radiusInfo = brush->GetRadius().X*4+brush->GetRadius().Y*4+7; + sim->CreateBox(position1.X, position1.Y, position2.X, position2.Y, toolID | (radiusInfo << 8), 0); +} +void Element_TESC_Tool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) { + int radiusInfo = brush->GetRadius().X*4+brush->GetRadius().Y*4+7; + sim->FloodParts(position.X, position.Y, toolID | (radiusInfo << 8), -1, -1, 0); +} + + +void PlopTool::Click(Simulation * sim, Brush * brush, ui::Point position) +{ + sim->create_part(-1, position.X, position.Y, toolID); +}
\ No newline at end of file diff --git a/src/gui/game/Tool.h b/src/gui/game/Tool.h new file mode 100644 index 0000000..ea38fbc --- /dev/null +++ b/src/gui/game/Tool.h @@ -0,0 +1,183 @@ +#ifndef TOOL_H_ +#define TOOL_H_ + +#include <iostream> + +using namespace std; + +#include "gui/interface/Point.h" + +class Simulation; +class Brush; +class VideoBuffer; + +class Tool +{ +protected: + VideoBuffer * (*textureGen)(int, int, int); + int toolID; + string toolName; + string toolDescription; + float strength; + int resolution; + std::string identifier; +public: + Tool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL); + int GetToolID() { return toolID; } + string GetName(); + string GetDescription(); + std::string GetIdentifier(); + int GetResolution() { return resolution; } + void SetStrength(float value) { strength = value; } + float GetStrength() { return strength; } + VideoBuffer * GetTexture(int width, int height); + void SetTextureGen(VideoBuffer * (*textureGen)(int, int, int)); + virtual ~Tool(); + virtual void Click(Simulation * sim, Brush * brush, ui::Point position); + virtual void Draw(Simulation * sim, Brush * brush, ui::Point position); + virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false); + virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2); + virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position); + int colRed, colBlue, colGreen; +}; + +class SignTool: public Tool +{ +public: + SignTool(): + Tool(0, "SIGN", "Sign. Click a sign to edit or anywhere else to create a new one", 0, 0, 0, "DEFAULT_UI_SIGN", SignTool::GetIcon) + { + } + static VideoBuffer * GetIcon(int toolID, int width, int height); + virtual ~SignTool() {} + virtual void Click(Simulation * sim, Brush * brush, ui::Point position); + virtual void Draw(Simulation * sim, Brush * brush, ui::Point position) { } + virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) { } + virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { } + virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position) { } +}; + +class GameModel; + +class SampleTool: public Tool +{ + GameModel * gameModel; +public: + SampleTool(GameModel * model): + Tool(0, "SMPL", "Sample an element on the screen", 0, 0, 0, "DEFAULT_UI_SAMPLE", SampleTool::GetIcon), + gameModel(model) + { + } + static VideoBuffer * GetIcon(int toolID, int width, int height); + virtual ~SampleTool() {} + virtual void Click(Simulation * sim, Brush * brush, ui::Point position) { } + virtual void Draw(Simulation * sim, Brush * brush, ui::Point position); + virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) { } + virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { } + virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position) { } +}; + +class PropertyTool: public Tool +{ +public: + PropertyTool(): + Tool(0, "PROP", "Property Edit. Click to alter the properties of elements in the field", 0xfe, 0xa9, 0x00, "DEFAULT_UI_PROPERTY", NULL) + { + } + virtual ~PropertyTool() {} + virtual void Click(Simulation * sim, Brush * brush, ui::Point position); + virtual void Draw(Simulation * sim, Brush * brush, ui::Point position) {}; + virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) { } + virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { } + virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position) { } +}; + +class Element_LIGH_Tool: public Tool +{ + int nextUse; +public: + Element_LIGH_Tool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL): + Tool(id, name, description, r, g, b, identifier, textureGen), + nextUse(0) + { + } + virtual ~Element_LIGH_Tool() {} + virtual void Draw(Simulation * sim, Brush * brush, ui::Point position); + virtual void Click(Simulation * sim, Brush * brush, ui::Point position) { } + virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) { } + virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { } + virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position) { } +}; + + +class ElementTool: public Tool +{ +public: + ElementTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL); + virtual ~ElementTool(); + virtual void Draw(Simulation * sim, Brush * brush, ui::Point position); + virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false); + virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2); + virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position); +}; + +class Element_TESC_Tool: public ElementTool +{ +public: + Element_TESC_Tool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL); + virtual ~Element_TESC_Tool() {} + virtual void Draw(Simulation * sim, Brush * brush, ui::Point position); + virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false); + virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2); + virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position); +}; + +class PlopTool: public ElementTool +{ +public: + PlopTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL): + ElementTool(id, name, description, r, g, b, identifier, textureGen) + { + } + virtual ~PlopTool() {} + virtual void Draw(Simulation * sim, Brush * brush, ui::Point position) {} + virtual void Click(Simulation * sim, Brush * brush, ui::Point position); + virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) { } + virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { } + virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position) { } +}; + +class WallTool: public Tool +{ +public: + WallTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL); + virtual ~WallTool(); + virtual void Draw(Simulation * sim, Brush * brush, ui::Point position); + virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false); + virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2); + virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position); +}; + +class GolTool: public Tool +{ +public: + GolTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL); + virtual ~GolTool(); + virtual void Draw(Simulation * sim, Brush * brush, ui::Point position); + virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false); + virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2); + virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position); +}; + +class WindTool: public Tool +{ +public: + WindTool(int id, string name, string description, int r, int g, int b, std::string identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL); + virtual ~WindTool(); + virtual void Draw(Simulation * sim, Brush * brush, ui::Point position); + virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false); + virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2); + virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position); +}; + +#endif /* TOOL_H_ */ diff --git a/src/gui/game/ToolButton.cpp b/src/gui/game/ToolButton.cpp new file mode 100644 index 0000000..8539ac7 --- /dev/null +++ b/src/gui/game/ToolButton.cpp @@ -0,0 +1,91 @@ +#include "ToolButton.h" +#include "gui/interface/Keys.h" + +ToolButton::ToolButton(ui::Point position, ui::Point size, std::string text_, std::string toolTip): + ui::Button(position, size, text_, toolTip) +{ + SetSelectionState(-1); + Appearance.BorderActive = ui::Colour(255, 0, 0); +} + +void ToolButton::OnMouseClick(int x, int y, unsigned int button) +{ + isButtonDown = true; +} + +void ToolButton::OnMouseUnclick(int x, int y, unsigned int button) +{ + if(isButtonDown) + { + isButtonDown = false; + if(button == BUTTON_LEFT) + SetSelectionState(0); + if(button == BUTTON_RIGHT) + SetSelectionState(1); + if(button == BUTTON_MIDDLE) + SetSelectionState(2); + DoAction(); + } +} + +void ToolButton::Draw(const ui::Point& screenPos) +{ + Graphics * g = ui::Engine::Ref().g; + int totalColour = Appearance.BackgroundInactive.Blue + (3*Appearance.BackgroundInactive.Green) + (2*Appearance.BackgroundInactive.Red); + + if(Appearance.GetTexture()) + { + g->draw_image(Appearance.GetTexture(), screenPos.X+2, screenPos.Y+2, 255); + } + else + { + g->fillrect(screenPos.X+2, screenPos.Y+2, Size.X-4, Size.Y-4, Appearance.BackgroundInactive.Red, Appearance.BackgroundInactive.Green, Appearance.BackgroundInactive.Blue, Appearance.BackgroundInactive.Alpha); + } + + if(isMouseInside && currentSelection == -1) + { + g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, Appearance.BorderActive.Red, Appearance.BorderActive.Green, Appearance.BorderActive.Blue, Appearance.BorderActive.Alpha); + } + else + { + g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, Appearance.BorderInactive.Red, Appearance.BorderInactive.Green, Appearance.BorderInactive.Blue, Appearance.BorderInactive.Alpha); + } + + if (totalColour<544) + { + g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, buttonDisplayText.c_str(), 255, 255, 255, 255); + } + else + { + g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, buttonDisplayText.c_str(), 0, 0, 0, 255); + } +} + +void ToolButton::SetSelectionState(int state) +{ + currentSelection = state; + switch(state) + { + case 0: + Appearance.BorderInactive = ui::Colour(255, 0, 0); + break; + case 1: + Appearance.BorderInactive = ui::Colour(0, 0, 255); + break; + case 2: + Appearance.BorderInactive = ui::Colour(0, 255, 0); + break; + default: + Appearance.BorderInactive = ui::Colour(0, 0, 0); + break; + } +} + +int ToolButton::GetSelectionState() +{ + return currentSelection; +} + +ToolButton::~ToolButton() { +} + diff --git a/src/gui/game/ToolButton.h b/src/gui/game/ToolButton.h new file mode 100644 index 0000000..86c8970 --- /dev/null +++ b/src/gui/game/ToolButton.h @@ -0,0 +1,18 @@ +#ifndef TOOLBUTTON_H_ +#define TOOLBUTTON_H_ + +#include "gui/interface/Button.h" + +class ToolButton: public ui::Button { + int currentSelection; +public: + ToolButton(ui::Point position, ui::Point size, std::string text_, std::string toolTip = ""); + virtual void OnMouseUnclick(int x, int y, unsigned int button); + virtual void OnMouseClick(int x, int y, unsigned int button); + virtual void Draw(const ui::Point& screenPos); + void SetSelectionState(int state); + int GetSelectionState(); + virtual ~ToolButton(); +}; + +#endif /* TOOLBUTTON_H_ */ diff --git a/src/gui/game/TriangleBrush.h b/src/gui/game/TriangleBrush.h new file mode 100644 index 0000000..7d537bd --- /dev/null +++ b/src/gui/game/TriangleBrush.h @@ -0,0 +1,46 @@ +/* + * TriangleBrush.h + * + * Created on: Jan 26, 2012 + * Author: Savely Skresanov + */ + +#ifndef TRIANGLEBRUSH_H_ +#define TRIANGLEBRUSH_H_ + +#include <cmath> +#include "Brush.h" + +class TriangleBrush: public Brush +{ +public: + TriangleBrush(ui::Point size_): + Brush(size_) + { + SetRadius(size_); + }; + virtual void GenerateBitmap() + { + if(bitmap) + delete[] bitmap; + bitmap = new unsigned char[size.X*size.Y]; + int rx = radius.X; + int ry = radius.Y; + for(int x = -rx; x <= rx; x++) + { + for(int y = -ry; y <= ry; y++) + { + if ((abs((rx+2*x)*ry+rx*y) + abs(2*rx*(y-ry)) + abs((rx-2*x)*ry+rx*y))<=(4*rx*ry)) + { + bitmap[(y+ry)*(size.X)+x+rx] = 255; + } + else + { + bitmap[(y+ry)*(size.X)+x+rx] = 0; + } + } + } + } +}; + +#endif /* TRIANGLEBRUSH_H_ */ diff --git a/src/gui/interface/Appearance.cpp b/src/gui/interface/Appearance.cpp new file mode 100644 index 0000000..a4c21d0 --- /dev/null +++ b/src/gui/interface/Appearance.cpp @@ -0,0 +1,54 @@ +#include <iostream> +#include "Appearance.h" + +namespace ui +{ + Appearance::Appearance(): + HorizontalAlign(AlignCentre), + VerticalAlign(AlignMiddle), + + BackgroundHover(20, 20, 20), + BackgroundInactive(0, 0, 0), + BackgroundActive(255, 255, 255), + BackgroundDisabled(10, 10, 10), + + TextHover(255, 255, 255), + TextInactive(255, 255, 255), + TextActive(0, 0, 0), + TextDisabled(100, 100, 100), + + BorderHover(255, 255, 255), + BorderInactive(200, 200, 200), + BorderActive(235, 235, 235), + BorderDisabled(100, 100, 100), + + Margin(1, 4), + Border(1), + + icon(NoIcon), + + texture(NULL) + {}; + + VideoBuffer * Appearance::GetTexture() + { + return texture; + } + + void Appearance::SetTexture(VideoBuffer * texture) + { + if(this->texture) + delete this->texture; + if(texture) + this->texture = new VideoBuffer(texture); + else + this->texture = NULL; + } + + Appearance::~Appearance() + { + if(texture) + delete texture; + } + +} diff --git a/src/gui/interface/Appearance.h b/src/gui/interface/Appearance.h new file mode 100644 index 0000000..1c1bb5a --- /dev/null +++ b/src/gui/interface/Appearance.h @@ -0,0 +1,57 @@ +#ifndef APPEARANCE_H_ +#define APPEARANCE_H_ + +#include "Border.h" +#include "Colour.h" +#include "graphics/Graphics.h" + +namespace ui +{ + class Appearance + { + private: + VideoBuffer * texture; + public: + enum HorizontalAlignment + { + AlignLeft, AlignCentre, AlignRight + }; + + enum VerticalAlignment + { + AlignTop, AlignMiddle, AlignBottom + }; + + VerticalAlignment VerticalAlign; + HorizontalAlignment HorizontalAlign; + + ui::Colour BackgroundHover; + ui::Colour BackgroundInactive; + ui::Colour BackgroundActive; + ui::Colour BackgroundDisabled; + + ui::Colour TextHover; + ui::Colour TextInactive; + ui::Colour TextActive; + ui::Colour TextDisabled; + + ui::Colour BorderHover; + ui::Colour BorderInactive; + ui::Colour BorderActive; + ui::Colour BorderDisabled; + + ui::Border Margin; + + ui::Border Border; + + Icon icon; + + VideoBuffer * GetTexture(); + void SetTexture(VideoBuffer * texture); + + Appearance(); + ~Appearance(); + }; +} + +#endif diff --git a/src/gui/interface/AvatarButton.cpp b/src/gui/interface/AvatarButton.cpp new file mode 100644 index 0000000..e385c77 --- /dev/null +++ b/src/gui/interface/AvatarButton.cpp @@ -0,0 +1,117 @@ +#include <iostream> +#include <typeinfo> + +#include "AvatarButton.h" +#include "Format.h" +#include "Engine.h" +#include "client/Client.h" +#include "client/requestbroker/RequestBroker.h" +#include "graphics/Graphics.h" +#include "ContextMenu.h" +#include "Keys.h" + +namespace ui { + +AvatarButton::AvatarButton(Point position, Point size, std::string username): + Component(position, size), + name(username), + actionCallback(NULL), + avatar(NULL), + tried(false) +{ + +} + +AvatarButton::~AvatarButton() +{ + RequestBroker::Ref().DetachRequestListener(this); + if(avatar) + delete avatar; + if(actionCallback) + delete actionCallback; +} + +void AvatarButton::Tick(float dt) +{ + if(!avatar && !tried && name.size() > 0) + { + tried = true; + RequestBroker::Ref().RetrieveAvatar(name, Size.X, Size.Y, this); + } +} + +void AvatarButton::OnResponseReady(void * imagePtr) +{ + VideoBuffer * image = (VideoBuffer*)imagePtr; + if(image) + { + if(avatar) + delete avatar; + avatar = image; + } +} + +void AvatarButton::Draw(const Point& screenPos) +{ + Graphics * g = ui::Engine::Ref().g; + + if(avatar) + { + g->draw_image(avatar, screenPos.X, screenPos.Y, 255); + } +} + +void AvatarButton::OnMouseUnclick(int x, int y, unsigned int button) +{ + if(button != 1) + { + return; //left click only! + } + + if(isButtonDown) + { + isButtonDown = false; + DoAction(); + } +} + +void AvatarButton::OnContextMenuAction(int item) +{ + //Do nothing +} + +void AvatarButton::OnMouseClick(int x, int y, unsigned int button) +{ + if(button == BUTTON_RIGHT) + { + if(menu) + menu->Show(GetScreenPos() + ui::Point(x, y)); + } + else + { + isButtonDown = true; + } +} + +void AvatarButton::OnMouseEnter(int x, int y) +{ + isMouseInside = true; +} + +void AvatarButton::OnMouseLeave(int x, int y) +{ + isMouseInside = false; +} + +void AvatarButton::DoAction() +{ + if(actionCallback) + actionCallback->ActionCallback(this); +} + +void AvatarButton::SetActionCallback(AvatarButtonAction * action) +{ + actionCallback = action; +} + +} /* namespace ui */ diff --git a/src/gui/interface/AvatarButton.h b/src/gui/interface/AvatarButton.h new file mode 100644 index 0000000..26b4348 --- /dev/null +++ b/src/gui/interface/AvatarButton.h @@ -0,0 +1,54 @@ +#ifndef AVATARBUTTON_H_ +#define AVATARBUTTON_H_ + +#include <string> + +#include "Component.h" +#include "graphics/Graphics.h" +#include "gui/interface/Colour.h" +#include "client/requestbroker/RequestListener.h" + +namespace ui +{ +class AvatarButton; +class AvatarButtonAction +{ +public: + virtual void ActionCallback(ui::AvatarButton * sender) {} + virtual ~AvatarButtonAction() {} +}; + +class AvatarButton : public Component, public RequestListener +{ + VideoBuffer * avatar; + std::string name; + bool tried; +public: + AvatarButton(Point position, Point size, std::string username); + virtual ~AvatarButton(); + + virtual void OnMouseClick(int x, int y, unsigned int button); + virtual void OnMouseUnclick(int x, int y, unsigned int button); + + virtual void OnMouseEnter(int x, int y); + virtual void OnMouseLeave(int x, int y); + + virtual void OnContextMenuAction(int item); + + virtual void Draw(const Point& screenPos); + virtual void Tick(float dt); + + virtual void OnResponseReady(void * imagePtr); + + virtual void DoAction(); + + void SetUsername(std::string username) { name = username; } + std::string GetUsername() { return name; } + void SetActionCallback(AvatarButtonAction * action); +protected: + bool isMouseInside, isButtonDown; + AvatarButtonAction * actionCallback; +}; +} +#endif /* AVATARBUTTON_H_ */ + diff --git a/src/gui/interface/Border.h b/src/gui/interface/Border.h new file mode 100644 index 0000000..a1ceb81 --- /dev/null +++ b/src/gui/interface/Border.h @@ -0,0 +1,69 @@ +#pragma once +#include "Platform.h" + +namespace ui +{ + + struct Border + { +#if ENABLE_FLOAT_UI +# define BORDER_T float +#else +# define BORDER_T int +#endif + + BORDER_T Top; + BORDER_T Right; + BORDER_T Bottom; + BORDER_T Left; + + Border(BORDER_T all): + Top(all), + Right(all), + Bottom(all), + Left(all) + { + } + + Border(BORDER_T v, BORDER_T h): + Top(v), + Right(h), + Bottom(v), + Left(h) + { + } + + Border(BORDER_T top, BORDER_T right, BORDER_T bottom, BORDER_T left): + Top(top), + Right(right), + Bottom(bottom), + Left(left) + { + } + + inline bool operator == (const int& v) const + { + return (Top == v && Right == v && Bottom == v && Left == v); + } + + inline bool operator == (const Border& v) const + { + return (Top == v.Top && Right == v.Right && Bottom == v.Bottom && Left == v.Left); + } + + inline bool operator != (const Border& v) const + { + return (Top != v.Top || Right != v.Right || Bottom != v.Bottom || Left != v.Left); + } + + inline void operator = (const Border& v) + { + Top = v.Top; + Right = v.Right; + Bottom = v.Bottom; + Left = v.Left; + } + + }; + +} diff --git a/src/gui/interface/Button.cpp b/src/gui/interface/Button.cpp new file mode 100644 index 0000000..3f08153 --- /dev/null +++ b/src/gui/interface/Button.cpp @@ -0,0 +1,237 @@ +#include <iostream> +#include "gui/interface/Button.h" +#include "graphics/Graphics.h" +#include "Engine.h" +#include "Misc.h" + +namespace ui { + +Button::Button(Point position, Point size, std::string buttonText, std::string toolTip): + Component(position, size), + ButtonText(buttonText), + isMouseInside(false), + isButtonDown(false), + isTogglable(false), + toggle(false), + actionCallback(NULL), + Enabled(true), + toolTip(toolTip) +{ + TextPosition(); +} + +void Button::TextPosition() +{ + buttonDisplayText = ButtonText; + if(buttonDisplayText.length()) + { + if(Graphics::textwidth((char *)buttonDisplayText.c_str()) > Size.X - (Appearance.icon? 22 : 0)) + { + int position = Graphics::textwidthx((char *)buttonDisplayText.c_str(), Size.X - (Appearance.icon? 38 : 22)); + buttonDisplayText = buttonDisplayText.erase(position, buttonDisplayText.length()-position); + buttonDisplayText += "..."; + } + } + + Component::TextPosition(buttonDisplayText); +} + +void Button::SetIcon(Icon icon) +{ + Appearance.icon = icon; + TextPosition(); +} + +void Button::SetText(std::string buttonText) +{ + ButtonText = buttonText; + TextPosition(); +} + +void Button::SetTogglable(bool togglable) +{ + toggle = false; + isTogglable = togglable; +} + +bool Button::GetTogglable() +{ + return isTogglable; +} + +TPT_NO_INLINE bool Button::GetToggleState() +{ + return toggle; +} + +TPT_NO_INLINE void Button::SetToggleState(bool state) +{ + toggle = state; +} + +void Button::Draw(const Point& screenPos) +{ + if(!drawn) + { + TextPosition(); + drawn = true; + } + Graphics * g = ui::Engine::Ref().g; + Point Position = screenPos; + ui::Colour bgColour(0, 0, 0); + + ui::Colour textColour = Appearance.TextInactive; + ui::Colour borderColour = Appearance.BorderInactive; + ui::Colour backgroundColour = Appearance.BackgroundInactive; + + if(Enabled) + { + if(isButtonDown || (isTogglable && toggle)) + { + textColour = Appearance.TextActive; + borderColour = Appearance.BorderActive; + backgroundColour = Appearance.BackgroundActive; + } + else if (isMouseInside) + { + textColour = Appearance.TextHover; + borderColour = Appearance.BorderHover; + backgroundColour = Appearance.BackgroundHover; + } + else + { + textColour = Appearance.TextInactive; + borderColour = Appearance.BorderInactive; + backgroundColour = Appearance.BackgroundInactive; + } + } + else + { + textColour = Appearance.TextDisabled; + borderColour = Appearance.BorderDisabled; + backgroundColour = Appearance.BackgroundDisabled; + } + + bgColour = Appearance.BackgroundInactive; + g->fillrect(Position.X+1, Position.Y+1, Size.X-2, Size.Y-2, backgroundColour.Red, backgroundColour.Green, backgroundColour.Blue, backgroundColour.Alpha); + if(Appearance.Border == 1) + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha); + else + { + if(Appearance.Border.Top) + g->draw_line(Position.X, Position.Y, Position.X+Size.X-1, Position.Y, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha); + if(Appearance.Border.Bottom) + g->draw_line(Position.X, Position.Y+Size.Y-1, Position.X+Size.X-1, Position.Y+Size.Y-1, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha); + if(Appearance.Border.Left) + g->draw_line(Position.X, Position.Y, Position.X, Position.Y+Size.Y-1, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha); + if(Appearance.Border.Right) + g->draw_line(Position.X+Size.X-1, Position.Y, Position.X+Size.X-1, Position.Y+Size.Y-1, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha); + } + g->drawtext(Position.X+textPosition.X, Position.Y+textPosition.Y, buttonDisplayText, textColour.Red, textColour.Green, textColour.Blue, textColour.Alpha); + + bool iconInvert = (backgroundColour.Blue + (3*backgroundColour.Green) + (2*backgroundColour.Red))>544?true:false; + + if(Appearance.icon) + { + if(Enabled) + if(isButtonDown || (isTogglable && toggle)) + { + g->draw_icon(Position.X+iconPosition.X, Position.Y+iconPosition.Y, Appearance.icon, 255, iconInvert); + } + else + { + g->draw_icon(Position.X+iconPosition.X, Position.Y+iconPosition.Y, Appearance.icon, 255, iconInvert); + } + else + g->draw_icon(Position.X+iconPosition.X, Position.Y+iconPosition.Y, Appearance.icon, 180, iconInvert); + } +} + +void Button::OnMouseUnclick(int x, int y, unsigned int button) +{ + if(button == 1) + { + if(isButtonDown) + { + isButtonDown = false; + DoAction(); + } + } + else if(button == 3) + { + if(isAltButtonDown) + { + isAltButtonDown = false; + DoAltAction(); + } + } +} + +void Button::OnMouseClick(int x, int y, unsigned int button) +{ + if(!Enabled) + return; + if(button == 1) + { + if(isTogglable) + { + toggle = !toggle; + } + isButtonDown = true; + } + else if(button == 3) + { + isAltButtonDown = true; + } +} + +void Button::OnMouseEnter(int x, int y) +{ + isMouseInside = true; + if(!Enabled) + return; + if(actionCallback) + actionCallback->MouseEnterCallback(this); + if(toolTip.length()>0 && GetParentWindow()) + { + GetParentWindow()->ToolTip(this, ui::Point(x, y), toolTip); + } +} + + +void Button::OnMouseLeave(int x, int y) +{ + isMouseInside = false; + isButtonDown = false; +} + +void Button::DoAction() +{ + if(!Enabled) + return; + if(actionCallback) + actionCallback->ActionCallback(this); +} + +void Button::DoAltAction() +{ + if(!Enabled) + return; + if(actionCallback) + actionCallback->AltActionCallback(this); +} + +void Button::SetActionCallback(ButtonAction * action) +{ + if(actionCallback) + delete actionCallback; + actionCallback = action; +} + +Button::~Button() +{ + if(actionCallback) + delete actionCallback; +} + +} /* namespace ui */ diff --git a/src/gui/interface/Button.h b/src/gui/interface/Button.h new file mode 100644 index 0000000..05f466d --- /dev/null +++ b/src/gui/interface/Button.h @@ -0,0 +1,63 @@ +#ifndef BUTTON_H_ +#define BUTTON_H_ + +#include <string> +#include "Misc.h" +#include "Component.h" +#include "Colour.h" + +namespace ui +{ +class Button; +class ButtonAction +{ +public: + virtual void ActionCallback(ui::Button * sender) {} + virtual void AltActionCallback(ui::Button * sender) {} + virtual void MouseEnterCallback(ui::Button * sender) {} + virtual ~ButtonAction() {} +}; + +class Button : public Component +{ +public: + Button(Point position = Point(0, 0), Point size = Point(0, 0), std::string buttonText = "", std::string toolTip = ""); + virtual ~Button(); + + bool Toggleable; + bool Enabled; + + virtual void OnMouseClick(int x, int y, unsigned int button); + virtual void OnMouseUnclick(int x, int y, unsigned int button); + //virtual void OnMouseUp(int x, int y, unsigned int button); + + virtual void OnMouseEnter(int x, int y); + virtual void OnMouseLeave(int x, int y); + + virtual void Draw(const Point& screenPos); + + virtual void TextPosition(); + inline bool GetState() { return state; } + virtual void DoAction(); //action of button what ever it may be + virtual void DoAltAction(); //action of button what ever it may be + void SetTogglable(bool isTogglable); + bool GetTogglable(); + TPT_NO_INLINE bool GetToggleState(); + TPT_NO_INLINE void SetToggleState(bool state); + void SetActionCallback(ButtonAction * action); + ButtonAction * GetActionCallback() { return actionCallback; } + void SetText(std::string buttonText); + void SetIcon(Icon icon); + inline std::string GetText() { return ButtonText; }; +protected: + + std::string toolTip; + std::string buttonDisplayText; + std::string ButtonText; + + bool isButtonDown, isAltButtonDown, state, isMouseInside, isTogglable, toggle; + ButtonAction * actionCallback; + +}; +} +#endif /* BUTTON_H_ */ diff --git a/src/gui/interface/Checkbox.cpp b/src/gui/interface/Checkbox.cpp new file mode 100644 index 0000000..7caf18b --- /dev/null +++ b/src/gui/interface/Checkbox.cpp @@ -0,0 +1,104 @@ +#include "Checkbox.h" + +using namespace ui; + +Checkbox::Checkbox(ui::Point position, ui::Point size, std::string text, std::string toolTip): + Component(position, size), + text(text), + toolTip(toolTip), + isMouseOver(false), + checked(false), + actionCallback(NULL) +{ + +} + +void Checkbox::SetText(std::string text) +{ + this->text = text; +} + +std::string Checkbox::GetText() +{ + return text; +} + +void Checkbox::SetIcon(Icon icon) +{ + Appearance.icon = icon; + iconPosition.X = 16; + iconPosition.Y = 3; +} + +void Checkbox::OnMouseClick(int x, int y, unsigned int button) +{ + if(checked) + { + checked = false; + } + else + { + checked = true; + } + if(actionCallback) + actionCallback->ActionCallback(this); +} + +void Checkbox::OnMouseUp(int x, int y, unsigned int button) +{ + +} + + +void Checkbox::OnMouseEnter(int x, int y) +{ + isMouseOver = true; + if(toolTip.length()>0 && GetParentWindow()) + { + GetParentWindow()->ToolTip(this, ui::Point(x, y), toolTip); + } +} + +void Checkbox::OnMouseLeave(int x, int y) +{ + isMouseOver = false; +} + +void Checkbox::Draw(const Point& screenPos) +{ + Graphics * g = Engine::Ref().g; + if(checked) + { + g->fillrect(screenPos.X+5, screenPos.Y+5, 6, 6, 255, 255, 255, 255); + } + if(isMouseOver) + { + g->drawrect(screenPos.X+2, screenPos.Y+2, 12, 12, 255, 255, 255, 255); + g->fillrect(screenPos.X+5, screenPos.Y+5, 6, 6, 255, 255, 255, 170); + if (!Appearance.icon) + g->drawtext(screenPos.X+18, screenPos.Y+4, text, 255, 255, 255, 255); + else + g->draw_icon(screenPos.X+iconPosition.X, screenPos.Y+iconPosition.Y, Appearance.icon, 255); + } + else + { + g->drawrect(screenPos.X+2, screenPos.Y+2, 12, 12, 255, 255, 255, 200); + if (!Appearance.icon) + g->drawtext(screenPos.X+18, screenPos.Y+4, text, 255, 255, 255, 200); + else + g->draw_icon(screenPos.X+iconPosition.X, screenPos.Y+iconPosition.Y, Appearance.icon, 200); + } +} + +void Checkbox::SetActionCallback(CheckboxAction * action) +{ + if(actionCallback) + delete actionCallback; + actionCallback = action; +} + +Checkbox::~Checkbox() { + if(actionCallback) + delete actionCallback; +} + diff --git a/src/gui/interface/Checkbox.h b/src/gui/interface/Checkbox.h new file mode 100644 index 0000000..f82cef7 --- /dev/null +++ b/src/gui/interface/Checkbox.h @@ -0,0 +1,39 @@ +#ifndef CHECKBOX_H_ +#define CHECKBOX_H_ + +#include <string> +#include "Component.h" +namespace ui +{ +class Checkbox; +class CheckboxAction +{ +public: + virtual void ActionCallback(ui::Checkbox * sender) {} + virtual ~CheckboxAction() {} +}; +class Checkbox: public ui::Component { + std::string text; + std::string toolTip; + bool checked; + bool isMouseOver; + CheckboxAction * actionCallback; +public: + Checkbox(ui::Point position, ui::Point size, std::string text, std::string toolTip); + void SetText(std::string text); + std::string GetText(); + void SetIcon(Icon icon); + void Draw(const Point& screenPos); + virtual void OnMouseEnter(int x, int y); + virtual void OnMouseLeave(int x, int y); + virtual void OnMouseClick(int x, int y, unsigned int button); + virtual void OnMouseUp(int x, int y, unsigned int button); + void SetActionCallback(CheckboxAction * action); + CheckboxAction * GetActionCallback() { return actionCallback; } + bool GetChecked() { return checked; } + void SetChecked(bool checked_) { checked = checked_; } + virtual ~Checkbox(); +}; +} + +#endif /* CHECKBOX_H_ */ diff --git a/src/gui/interface/Colour.h b/src/gui/interface/Colour.h new file mode 100644 index 0000000..194b9c9 --- /dev/null +++ b/src/gui/interface/Colour.h @@ -0,0 +1,24 @@ +#ifndef COLOUR_H +#define COLOUR_H + +namespace ui +{ +class Colour +{ +public: + unsigned char Red, Green, Blue, Alpha; + Colour(unsigned char red, unsigned char green, unsigned char blue): + Red(red), Green(green), Blue(blue), Alpha(255) + { + } + Colour(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha): + Red(red), Green(green), Blue(blue), Alpha(alpha) + { + } + Colour() + { + } +}; +} + +#endif diff --git a/src/gui/interface/Component.cpp b/src/gui/interface/Component.cpp new file mode 100644 index 0000000..6c4c548 --- /dev/null +++ b/src/gui/interface/Component.cpp @@ -0,0 +1,244 @@ +//#include "Platform.h" +#include <iostream> +#include "gui/interface/Component.h" +#include "gui/interface/Engine.h" +#include "gui/interface/Point.h" +#include "gui/interface/Window.h" +#include "gui/interface/Panel.h" +#include "gui/interface/ContextMenu.h" + +using namespace ui; + +Component::Component(Window* parent_state): + parentstate_(parent_state), + _parent(NULL), + Position(Point(0,0)), + Size(Point(0,0)), + Locked(false), + Visible(true), + textPosition(0, 0), + textSize(0, 0), + iconPosition(0, 0), + drawn(false), + menu(NULL) +{ + +} + +Component::Component(Point position, Point size): + parentstate_(0), + _parent(NULL), + Position(position), + Size(size), + Locked(false), + Visible(true), + textPosition(0, 0), + textSize(0, 0), + iconPosition(0, 0), + drawn(false), + menu(NULL) +{ + +} + +Component::Component(): + parentstate_(NULL), + _parent(NULL), + Position(Point(0,0)), + Size(Point(0,0)), + Locked(false), + Visible(true), + textPosition(0, 0), + textSize(0, 0), + iconPosition(0, 0), + drawn(false), + menu(NULL) +{ + +} + +void Component::Refresh() +{ + drawn = false; +} + +void Component::TextPosition(std::string displayText) +{ + + textPosition = ui::Point(0, 0); + + int textWidth, textHeight = 10; + Graphics::textsize((char*)displayText.c_str(), textWidth, textHeight); + textSize.X = textWidth; textSize.Y = textHeight; + textHeight-=3; + textWidth-=1; + if(Appearance.icon) + { + textWidth += 13; + } + + int textAreaWidth = Size.X-(Appearance.Margin.Right+Appearance.Margin.Left); + int textAreaHeight = Size.Y-(Appearance.Margin.Top+Appearance.Margin.Bottom); + + switch(Appearance.VerticalAlign) + { + case ui::Appearance::AlignTop: + textPosition.Y = Appearance.Margin.Top+2; + break; + case ui::Appearance::AlignMiddle: + textPosition.Y = Appearance.Margin.Top+((textAreaHeight-textHeight)/2); + break; + case ui::Appearance::AlignBottom: + textPosition.Y = Size.Y-(textHeight+Appearance.Margin.Bottom); + break; + } + + switch(Appearance.HorizontalAlign) + { + case ui::Appearance::AlignLeft: + textPosition.X = Appearance.Margin.Left; + break; + case ui::Appearance::AlignCentre: + textPosition.X = Appearance.Margin.Left+((textAreaWidth-textWidth)/2); + break; + case ui::Appearance::AlignRight: + textPosition.X = Size.X-(textWidth+Appearance.Margin.Right); + break; + } + if(Appearance.icon) + { + iconPosition = textPosition-ui::Point(0, 1); + textPosition.X += 15; + } +} + +bool Component::IsFocused() const +{ + if(parentstate_) + return parentstate_->IsFocused(this); + return false; +} + +void Component::SetParentWindow(Window* window) +{ + parentstate_ = window; +} + +void Component::SetParent(Panel* new_parent) +{ + if(new_parent == NULL) + { + if(_parent != NULL) + { + // remove from current parent and send component to parent state + for(int i = 0; i < _parent->GetChildCount(); ++i) + { + if(_parent->GetChild(i) == this) + { + // remove ourself from parent component + _parent->RemoveChild(i, false); + + // add ourself to the parent state + GetParentWindow()->AddComponent(this); + + //done in this loop. + break; + } + } + } + } + else + { + // remove from parent state (if in parent state) and place in new parent + if(GetParentWindow()) + GetParentWindow()->RemoveComponent(this); + new_parent->children.push_back(this); + } + this->_parent = new_parent; +} + +Point Component::GetScreenPos() +{ + Point newPos(0,0); + if(GetParentWindow()) + newPos += GetParentWindow()->Position; + if(GetParent()) + newPos += GetParent()->Position + GetParent()->ViewportPosition; + newPos += Position; + return newPos; +} + +// ***** OVERRIDEABLES ***** +// Kept empty. + +void Component::OnContextMenuAction(int item) +{ + +} + +void Component::Draw(const Point& screenPos) +{ + drawn = true; +} + +void Component::Tick(float dt) +{ +} + +void Component::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ +} + +void Component::OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ +} + +void Component::OnMouseClick(int localx, int localy, unsigned button) +{ +} + +void Component::OnMouseDown(int x, int y, unsigned button) +{ +} + +void Component::OnMouseHover(int localx, int localy) +{ +} + +void Component::OnMouseMoved(int localx, int localy, int dx, int dy) +{ +} + +void Component::OnMouseMovedInside(int localx, int localy, int dx, int dy) +{ +} + +void Component::OnMouseEnter(int localx, int localy) +{ +} + +void Component::OnMouseLeave(int localx, int localy) +{ +} + +void Component::OnMouseUnclick(int localx, int localy, unsigned button) +{ +} + +void Component::OnMouseUp(int x, int y, unsigned button) +{ +} + +void Component::OnMouseWheel(int localx, int localy, int d) +{ +} + +void Component::OnMouseWheelInside(int localx, int localy, int d) +{ +} + +Component::~Component() +{ + if(menu) + delete menu; +} diff --git a/src/gui/interface/Component.h b/src/gui/interface/Component.h new file mode 100644 index 0000000..c034952 --- /dev/null +++ b/src/gui/interface/Component.h @@ -0,0 +1,224 @@ +#pragma once + +#include "Appearance.h" +#include "Point.h" +#include "Window.h" +#include "Platform.h" + +namespace ui +{ + class ContextMenu; + class Window; + class Panel; + + /* class Component + * + * An interactive UI component that can be added to a state or an XComponent*. + * *See sys::XComponent + */ + class Component + { + private: + Window* parentstate_; + Panel* _parent; + protected: + bool drawn; + ui::Point textPosition; + ui::Point textSize; + ui::Point iconPosition; + ui::ContextMenu * menu; + public: + Component(Window* parent_state); + Component(Point position, Point size); + Component(); + virtual ~Component(); + + void* UserData; + inline Window* const GetParentWindow() const { return parentstate_; } + bool IsFocused() const; + + void Invalidate() { drawn = false; } + + Point Position; + Point Size; + bool Locked; + bool Visible; + + ui::Appearance Appearance; + //virtual void SetAppearance(ui::Appearance); + //ui::Appearance GetAppearance(); + virtual void TextPosition(std::string); + + void Refresh(); + + Point GetScreenPos(); + + /* See the parent of this component. + * If new_parent is NULL, this component will have no parent. (THIS DOES NOT delete THE COMPONENT. See XComponent::RemoveChild) + */ + void SetParentWindow(Window* window); + void SetParent(Panel* new_parent); + + //Get the parent component. + inline Panel* const GetParent() const { return _parent; } + + virtual void OnContextMenuAction(int item); + + //UI functions: + /* + void Tick(float dt); + void Draw(const Point& screenPos); + + void OnMouseHover(int localx, int localy); + void OnMouseMoved(int localx, int localy, int dx, int dy); + void OnMouseMovedInside(int localx, int localy, int dx, int dy); + void OnMouseEnter(int localx, int localy); + void OnMouseLeave(int localx, int localy); + void OnMouseDown(int x, int y, unsigned int button); + void OnMouseUp(int x, int y, unsigned int button); + void OnMouseClick(int localx, int localy, unsigned int button); + void OnMouseUnclick(int localx, int localy, unsigned int button); + void OnMouseWheel(int localx, int localy, int d); + void OnMouseWheelInside(int localx, int localy, int d); + void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + void OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt); + */ + + /// + // Called: Every tick. + // Params: + // dt: The change in time. + /// + virtual void Tick(float dt); + + /// + // Called: When ready to draw. + // Params: + // None + /// + virtual void Draw(const Point& screenPos); + + + + + /// + // Called: When the mouse is currently hovering over the item. (Called every tick) + // Params: + // localx: Local mouse X position. + // localy: Local mouse Y position. + /// + virtual void OnMouseHover(int localx, int localy); + + /// + // Called: When the mouse moves. + // Params: + // localx: Local mouse X position. + // localy: Local mouse Y position. + // dx: Mouse X delta. + // dy: Mouse Y delta. + /// + virtual void OnMouseMoved(int localx, int localy, int dx, int dy); + + /// + // Called: When the mouse moves. + // Params: + // localx: Local mouse X position. + // localy: Local mouse Y position. + // dx: Mouse X delta. + // dy: Mouse Y delta. + /// + virtual void OnMouseMovedInside(int localx, int localy, int dx, int dy); + + /// + // Called: When the mouse moves on top of the item. + // Params: + // localx: Local mouse X position. + // localy: Local mouse Y position. + // dx: Mouse X delta. + // dy: Mouse Y delta. + /// + virtual void OnMouseEnter(int localx, int localy); + + /// + // Called: When the mouse leaves the item. + // Params: + // localx: Local mouse X position. + // localy: Local mouse Y position. + /// + virtual void OnMouseLeave(int localx, int localy); + + /// + // Called: When a mouse button is pressed. + // Params: + // x: X position of the mouse. + // y: Y position of the mouse. + // button: The button that is being held down. + /// + virtual void OnMouseDown(int x, int y, unsigned button); + + /// + // Called: When a mouse button is released. + // Params: + // x: X position of the mouse. + // y: Y position of the mouse. + // button: The button that is being released. + /// + virtual void OnMouseUp(int x, int y, unsigned button); + + /// + // Called: When a mouse button is pressed on top of the item. + // Params: + // x: X position of the mouse. + // y: Y position of the mouse. + // button: The button that is being held down. + /// + virtual void OnMouseClick(int localx, int localy, unsigned button); + + /// + // Called: When a mouse button is released on top of the item. + // Params: + // x: X position of the mouse. + // y: Y position of the mouse. + // button: The button that is being released. + /// + virtual void OnMouseUnclick(int localx, int localy, unsigned button); + + /// + // Called: When the mouse wheel moves/changes. + // Params: + // localx: Local mouse X position. + // localy: Local mouse Y position. + // d: The mouse wheel movement value. + /// + virtual void OnMouseWheel(int localx, int localy, int d); + + /// + // Called: When the mouse wheel moves/changes on top of the item. + // Params: + // localx: Local mouse X position. + // localy: Local mouse Y position. + // d: The mouse wheel movement value. + /// + virtual void OnMouseWheelInside(int localx, int localy, int d); + + /// + // Called: When a key is pressed. + // Params: + // key: The value of the key that is being pressed. + // shift: Shift key is down. + // ctrl: Control key is down. + // alt: Alternate key is down. + /// + virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + + /// + // Called: When a key is released. + // Params: + // key: The value of the key that is being released. + // shift: Shift key is released. + // ctrl: Control key is released. + // alt: Alternate key is released. + /// + virtual void OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt); + }; +} diff --git a/src/gui/interface/ContextMenu.cpp b/src/gui/interface/ContextMenu.cpp new file mode 100644 index 0000000..0d34e19 --- /dev/null +++ b/src/gui/interface/ContextMenu.cpp @@ -0,0 +1,99 @@ +#include "ContextMenu.h" + +using namespace ui; + +class ContextMenu::ItemSelectedAction: public ButtonAction +{ + ContextMenu * window; + int item; +public: + ItemSelectedAction(ContextMenu * window, int itemID): window(window), item(itemID) { } + virtual void ActionCallback(ui::Button *sender) + { + window->ActionCallback(sender, item); + } +}; + +ContextMenu::ContextMenu(Component * source): + Window(ui::Point(0, 0), ui::Point(0, 0)), + Appearance(source->Appearance), + source(source) +{ +} + +void ContextMenu::Show(ui::Point position) +{ + for(int i = 0; i < buttons.size(); i++) + { + RemoveComponent(buttons[i]); + delete buttons[i]; + } + buttons.clear(); + + Position = position; + Size.Y = items.size()*16; + Size.X = 100; + + int currentY = 1; + for(int i = 0; i < items.size(); i++) + { + Button * tempButton = new Button(Point(1, currentY), Point(Size.X-2, 16), items[i].Text); + tempButton->Appearance = Appearance; + tempButton->Enabled = items[i].Enabled; + tempButton->SetActionCallback(new ItemSelectedAction(this, items[i].ID)); + buttons.push_back(tempButton); + AddComponent(tempButton); + currentY += 15; + } + + ui::Engine::Ref().ShowWindow(this); +} + +void ContextMenu::ActionCallback(ui::Button *sender, int item) +{ + ui::Engine::Ref().CloseWindow(); + Halt(); + source->OnContextMenuAction(item); +} + +void ContextMenu::OnMouseDown(int x, int y, unsigned button) +{ + if(!(x > Position.X && y > Position.Y && y < Position.Y+Size.Y && x < Position.X+Size.X)) //Clicked outside window + ui::Engine::Ref().CloseWindow(); +} + +void ContextMenu::SetItem(int id, std::string text) +{ + for(int i = 0; i < items.size(); i++) + { + if(items[i].ID == id) + { + items[i].Text = text; + break; + } + } +} + +void ContextMenu::RemoveItem(int id) +{ + for(int i = 0; i < items.size(); i++) + { + if(items[i].ID == id) + { + items.erase(items.begin()+i); + break; + } + } +} + +void ContextMenu::AddItem(ContextMenuItem item) +{ + items.push_back(item); +} + +void ContextMenu::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + g->fillrect(Position.X, Position.Y, Size.X, Size.Y, 100, 100, 100, 255); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, Appearance.BackgroundInactive.Red, Appearance.BackgroundInactive.Green, Appearance.BackgroundInactive.Blue, Appearance.BackgroundInactive.Alpha); +}
\ No newline at end of file diff --git a/src/gui/interface/ContextMenu.h b/src/gui/interface/ContextMenu.h new file mode 100644 index 0000000..e5549d3 --- /dev/null +++ b/src/gui/interface/ContextMenu.h @@ -0,0 +1,40 @@ +#ifndef The_Powder_Toy_ContextMenu_h +#define The_Powder_Toy_ContextMenu_h + +#include "Window.h" +#include "Appearance.h" +#include "Button.h" + +namespace ui +{ + +class ContextMenuItem +{ +public: + int ID; + std::string Text; + bool Enabled; + ContextMenuItem(std::string text, int id, bool enabled) : Text(text), ID(id), Enabled(enabled) {} +}; + +class ContextMenu: public ui::Window, public ButtonAction { + std::vector<Button*> buttons; + std::vector<ContextMenuItem> items; + bool isMouseInside; + ui::Component * source; +public: + ui::Appearance Appearance; + class ItemSelectedAction; + ContextMenu(Component * source); + virtual void ActionCallback(ui::Button *sender, int item); + virtual void AddItem(ContextMenuItem item); + virtual void RemoveItem(int id); + virtual void SetItem(int id, std::string text); + virtual void Show(ui::Point position); + virtual void OnDraw(); + virtual void OnMouseDown(int x, int y, unsigned button); + virtual ~ContextMenu() {} +}; +} + +#endif
\ No newline at end of file diff --git a/src/gui/interface/DropDown.cpp b/src/gui/interface/DropDown.cpp new file mode 100644 index 0000000..f1dda7c --- /dev/null +++ b/src/gui/interface/DropDown.cpp @@ -0,0 +1,197 @@ +#include <iostream> +#include "gui/Style.h" +#include "Button.h" +#include "DropDown.h" + +namespace ui { + +class ItemSelectedAction; +class DropDownWindow: public ui::Window { + friend class ItemSelectedAction; + Appearance appearance; + DropDown * dropDown; + std::vector<Button> buttons; + bool isMouseInside; +public: + class ItemSelectedAction: public ButtonAction + { + DropDownWindow * window; + std::string option; + public: + ItemSelectedAction(DropDownWindow * window, std::string option): window(window), option(option) { } + virtual void ActionCallback(ui::Button *sender) + { + ui::Engine::Ref().CloseWindow(); + window->setOption(option); + window->SelfDestruct(); + } + }; + DropDownWindow(DropDown * dropDown): + Window(ui::Point(dropDown->Position.X+dropDown->GetParentWindow()->Position.X-5, dropDown->Position.Y+dropDown->GetParentWindow()->Position.Y-3), ui::Point(dropDown->Size.X+10, 1+dropDown->options.size()*16)), + dropDown(dropDown), + appearance(dropDown->Appearance) + { + int currentY = 1; + for(int i = 0; i < dropDown->options.size(); i++) + { + Button * tempButton = new Button(Point(1, currentY), Point(Size.X-2, 16), dropDown->options[i].first); + tempButton->Appearance = appearance; + if(i) + tempButton->Appearance.Border = ui::Border(0, 1, 1, 1); + tempButton->SetActionCallback(new ItemSelectedAction(this, dropDown->options[i].first)); + AddComponent(tempButton); + currentY += 16; + } + } + virtual void OnDraw() + { + Graphics * g = ui::Engine::Ref().g; + g->clearrect(Position.X, Position.Y, Size.X, Size.Y); + } + void setOption(std::string option) + { + dropDown->SetOption(option); + if(dropDown->callback) + { + int optionIndex = 0; + for(optionIndex = 0; optionIndex < dropDown->options.size(); optionIndex++) + { + if(option == dropDown->options[optionIndex].first) + break; + } + dropDown->callback->OptionChanged(dropDown, dropDown->options[optionIndex]); + } + } + virtual void OnTryExit(ExitMethod method) + { + ui::Engine::Ref().CloseWindow(); + SelfDestruct(); + } + virtual ~DropDownWindow() {} +}; + +DropDown::DropDown(Point position, Point size): + Component(position, size), + isMouseInside(false), + optionIndex(-1), + callback(NULL) +{ +} + +void DropDown::OnMouseClick(int x, int y, unsigned int button) +{ + DropDownWindow * newWindow = new DropDownWindow(this); + ui::Engine::Ref().ShowWindow(newWindow); +} + +void DropDown::Draw(const Point& screenPos) +{ + if(!drawn) + { + if(optionIndex!=-1) + TextPosition(options[optionIndex].first); + drawn = true; + } + Graphics * g = ui::Engine::Ref().g; + Point Position = screenPos; + + ui::Colour textColour = Appearance.TextInactive; + ui::Colour borderColour = Appearance.BorderInactive; + ui::Colour backgroundColour = Appearance.BackgroundInactive; + + if (isMouseInside) + { + textColour = Appearance.TextHover; + borderColour = Appearance.BorderHover; + backgroundColour = Appearance.BackgroundHover; + } + else + { + textColour = Appearance.TextInactive; + borderColour = Appearance.BorderInactive; + backgroundColour = Appearance.BackgroundInactive; + } + + g->fillrect(Position.X-1, Position.Y-1, Size.X+2, Size.Y+2, backgroundColour.Red, backgroundColour.Green, backgroundColour.Blue, backgroundColour.Alpha); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha); + if(optionIndex!=-1) + g->drawtext(Position.X+textPosition.X, Position.Y+textPosition.Y, options[optionIndex].first, textColour.Red, textColour.Green, textColour.Blue, textColour.Alpha); +} + +void DropDown::OnMouseEnter(int x, int y) +{ + isMouseInside = true; +} + +void DropDown::OnMouseLeave(int x, int y) +{ + isMouseInside = false; +} + std::pair<std::string, int> DropDown::GetOption() + { + if(optionIndex!=-1) + { + return options[optionIndex]; + } + return std::pair<std::string, int>("", -1); + } + + void DropDown::SetOption(std::string option) + { + for(int i = 0; i < options.size(); i++) + { + if(options[i].first == option) + { + optionIndex = i; + TextPosition(options[optionIndex].first); + return; + } + } + } + void DropDown::SetOption(int option) + { + for(int i = 0; i < options.size(); i++) + { + if(options[i].second == option) + { + optionIndex = i; + TextPosition(options[optionIndex].first); + return; + } + } + } + void DropDown::AddOption(std::pair<std::string, int> option) + { + for(int i = 0; i < options.size(); i++) + { + if(options[i] == option) + return; + } + options.push_back(option); + } + void DropDown::RemoveOption(std::string option) + { + start: + for(int i = 0; i < options.size(); i++) + { + if(options[i].first == option) + { + if(i == optionIndex) + optionIndex = -1; + options.erase(options.begin()+i); + goto start; + } + } + } + void DropDown::SetOptions(std::vector<std::pair<std::string, int> > options) + { + this->options = options; + } + + +DropDown::~DropDown() { + if(callback) + delete callback; +} + +} /* namespace ui */ diff --git a/src/gui/interface/DropDown.h b/src/gui/interface/DropDown.h new file mode 100644 index 0000000..c13fdf6 --- /dev/null +++ b/src/gui/interface/DropDown.h @@ -0,0 +1,41 @@ +#ifndef DROPDOWN_H_ +#define DROPDOWN_H_ + +#include <utility> +#include "Component.h" +#include "Colour.h" + +namespace ui { + +class DropDown; +class DropDownWindow; +class DropDownAction +{ +public: + virtual void OptionChanged(DropDown * sender, std::pair<std::string, int> newOption) {} + virtual ~DropDownAction() {} +}; +class DropDown: public ui::Component { + friend class DropDownWindow; + bool isMouseInside; + int optionIndex; + DropDownAction * callback; + std::vector<std::pair<std::string, int> > options; +public: + DropDown(Point position, Point size); + std::pair<std::string, int> GetOption(); + void SetOption(int option); + void SetOption(std::string option); + void AddOption(std::pair<std::string, int> option); + void RemoveOption(std::string option); + void SetOptions(std::vector<std::pair<std::string, int> > options); + void SetActionCallback(DropDownAction * action) { callback = action;} + virtual void Draw(const Point& screenPos); + virtual void OnMouseClick(int x, int y, unsigned int button); + virtual void OnMouseEnter(int x, int y); + virtual void OnMouseLeave(int x, int y); + virtual ~DropDown(); +}; + +} /* namespace ui */ +#endif /* DROPDOWN_H_ */ diff --git a/src/gui/interface/Engine.cpp b/src/gui/interface/Engine.cpp new file mode 100644 index 0000000..6911c25 --- /dev/null +++ b/src/gui/interface/Engine.cpp @@ -0,0 +1,308 @@ +#include <iostream> +#include <stack> +#include <cstdio> +#include <time.h> + +#include "Config.h" +#include "gui/interface/Window.h" +#include "gui/interface/Platform.h" +#include "gui/interface/Engine.h" +#include "graphics/Graphics.h" + +using namespace ui; +using namespace std; + +Engine::Engine(): + state_(NULL), + maxWidth(0), + maxHeight(0), + mouseb_(0), + mousex_(0), + mousey_(0), + mousexp_(0), + mouseyp_(0), + FpsLimit(60.0f), + windows(stack<Window*>()), + mousePositions(stack<Point>()), + lastBuffer(NULL), + prevBuffers(stack<pixel*>()), + windowTargetPosition(0, 0), + FrameIndex(0), + Fullscreen(false), + Scale(1), + FastQuit(1), + break_(false), + lastTick(0) +{ +} + +Engine::~Engine() +{ + if(state_ != NULL) + delete state_; + //Dispose of any Windows. + while(!windows.empty()) + { + delete windows.top(); + windows.pop(); + } + if (lastBuffer) + free(lastBuffer); +} + +void Engine::Begin(int width, int height) +{ + //engine is now ready + running_ = true; + + width_ = width; + height_ = height; +} + +void Engine::Break() +{ + break_ = true; +} + +void Engine::UnBreak() +{ + break_ = false; +} + +void Engine::Exit() +{ + running_ = false; +} + +void Engine::ShowWindow(Window * window) +{ + windowOpenState = 0.0f; + if(window->Position.X==-1) + { + window->Position.X = (width_-window->Size.X)/2; + } + if(window->Position.Y==-1) + { + window->Position.Y = (height_-window->Size.Y)/2; + } + /*if(window->Position.Y > 0) + { + windowTargetPosition = window->Position; + window->Position = Point(windowTargetPosition.X, height_); + }*/ + if(state_) + { + if(lastBuffer) + { + prevBuffers.push(lastBuffer); + } + lastBuffer = (pixel*)malloc((width_ * height_) * PIXELSIZE); + +#ifndef OGLI + memcpy(lastBuffer, g->vid, (width_ * height_) * PIXELSIZE); +#endif + + windows.push(state_); + mousePositions.push(ui::Point(mousex_, mousey_)); + } + if(state_) + state_->DoBlur(); + + state_ = window; + +} + +int Engine::CloseWindow() +{ + if(!windows.empty()) + { + if (lastBuffer) + { + free(lastBuffer); + lastBuffer = NULL; + } + if(!prevBuffers.empty()) + { + lastBuffer = prevBuffers.top(); + prevBuffers.pop(); + } + state_ = windows.top(); + windows.pop(); + + if(state_) + state_->DoFocus(); + + ui::Point mouseState = mousePositions.top(); + mousePositions.pop(); + if(state_) + { + mousexp_ = mouseState.X; + mouseyp_ = mouseState.Y; + state_->DoMouseMove(mousex_, mousey_, mousex_ - mousexp_, mousey_ - mouseyp_); + mousexp_ = mousex_; + mouseyp_ = mousey_; + } + return 0; + } + else + { + state_ = NULL; + return 1; + } +} + +/*void Engine::SetState(State * state) +{ + if(state_) //queue if currently in a state + statequeued_ = state; + else + { + state_ = state; + if(state_) + state_->DoInitialized(); + } +}*/ + +void Engine::SetSize(int width, int height) +{ + width_ = width; + height_ = height; +} + +void Engine::SetMaxSize(int width, int height) +{ + maxWidth = width; + maxHeight = height; +} + +void Engine::Tick() +{ + if(state_ != NULL) + state_->DoTick(dt); + + + lastTick = clock(); + if(windowOpenState<1.0f) + { + if(lastBuffer) + { + pixel * vid = g->vid; + g->vid = lastBuffer; + g->fillrect(0, 0, width_, height_, 0, 0, 0, 5); + g->vid = vid; + + } + /*if(windowTargetPosition.Y < state_->Position.Y) + { + state_->Position.Y += windowTargetPosition.Y/20; + }*/ + windowOpenState += 0.05f;//*dt; + } + + /*if(statequeued_ != NULL) + { + if(state_ != NULL) + { + state_->DoExit(); + delete state_; + state_ = NULL; + } + state_ = statequeued_; + statequeued_ = NULL; + + if(state_ != NULL) + state_->DoInitialized(); + }*/ +} + +void Engine::Draw() +{ + if(lastBuffer && !(state_->Position.X == 0 && state_->Position.Y == 0 && state_->Size.X == width_ && state_->Size.Y == height_)) + { + g->Acquire(); + g->Clear(); +#ifndef OGLI + memcpy(g->vid, lastBuffer, (width_ * height_) * PIXELSIZE); +#endif + } + else + { + g->Clear(); + } + if(state_) + state_->DoDraw(); + +#ifdef DEBUG + char fpsText[512]; + sprintf(fpsText, "FPS: %.2f, Delta: %.3f", fps, dt); + ui::Engine::Ref().g->drawtext(10, 10, fpsText, 255, 255, 255, 255); +#endif + g->Finalise(); + g->Release(); + FrameIndex++; + FrameIndex %= 7200; +} + +void Engine::SetFps(float fps) +{ + this->fps = fps; + if(FpsLimit > 2.0f) + this->dt = FpsLimit/fps; + else + this->dt = 1.0f; +} + +void Engine::onKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + if(state_) + state_->DoKeyPress(key, character, shift, ctrl, alt); +} + +void Engine::onKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + if(state_) + state_->DoKeyRelease(key, character, shift, ctrl, alt); +} + +void Engine::onMouseClick(int x, int y, unsigned button) +{ + mouseb_ |= button; + if(state_) + state_->DoMouseDown(x, y, button); +} + +void Engine::onMouseUnclick(int x, int y, unsigned button) +{ + mouseb_ &= ~button; + if(state_) + state_->DoMouseUp(x, y, button); +} + +void Engine::onMouseMove(int x, int y) +{ + mousex_ = x; + mousey_ = y; + if(state_) + { + state_->DoMouseMove(x, y, mousex_ - mousexp_, mousey_ - mouseyp_); + } + mousexp_ = x; + mouseyp_ = y; +} + +void Engine::onMouseWheel(int x, int y, int delta) +{ + if(state_) + state_->DoMouseWheel(x, y, delta); +} + +void Engine::onResize(int newWidth, int newHeight) +{ + SetSize(newWidth, newHeight); +} + +void Engine::onClose() +{ + if(state_) + state_->DoExit(); +} diff --git a/src/gui/interface/Engine.h b/src/gui/interface/Engine.h new file mode 100644 index 0000000..fb110e4 --- /dev/null +++ b/src/gui/interface/Engine.h @@ -0,0 +1,108 @@ +#pragma once + +#include <stack> +#include "Singleton.h" +#include "Platform.h" +#include "graphics/Graphics.h" +#include "Window.h" + +namespace ui +{ + class Window; + + /* class Engine + * + * Controls the User Interface. + * Send user inputs to the Engine and the appropriate controls and components will interact. + */ + class Engine: public Singleton<Engine> + { + public: + Engine(); + ~Engine(); + + void ShowWindow(Window * window); + int CloseWindow(); + + void onMouseMove(int x, int y); + void onMouseClick(int x, int y, unsigned button); + void onMouseUnclick(int x, int y, unsigned button); + void onMouseWheel(int x, int y, int delta); + void onKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + void onKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt); + void onResize(int newWidth, int newHeight); + void onClose(); + + void Begin(int width, int height); + inline bool Running() { return running_; } + inline bool Broken() { return break_; } + inline int LastTick() { return lastTick; } + inline void LastTick(int tick) { lastTick = tick; } + void Exit(); + void Break(); + void UnBreak(); + + void SetFullscreen(bool fullscreen) { Fullscreen = fullscreen; } + inline bool GetFullscreen() { return Fullscreen; } + void SetScale(int scale) { Scale = scale; } + inline int GetScale() { return Scale; } + void SetFastQuit(bool fastquit) { FastQuit = fastquit; } + inline bool GetFastQuit() {return FastQuit; } + + void Tick(); + void Draw(); + + void SetFps(float fps); + inline float GetFps() { return fps; }; + + inline int GetMouseButton() { return mouseb_; } + inline int GetMouseX() { return mousex_; } + inline int GetMouseY() { return mousey_; } + inline int GetWidth() { return width_; } + inline int GetHeight() { return height_; } + inline int GetMaxWidth() { return maxWidth; } + inline int GetMaxHeight() { return maxHeight; } + + TPT_NO_INLINE void SetMaxSize(int width, int height); + + inline void SetSize(int width, int height); + + //void SetState(Window* state); + //inline State* GetState() { return state_; } + inline Window* GetWindow() { return state_; } + float FpsLimit; + Graphics * g; + int Scale; + bool Fullscreen; + + unsigned int FrameIndex; + private: + float dt; + float fps; + pixel * lastBuffer; + std::stack<pixel*> prevBuffers; + std::stack<Window*> windows; + std::stack<Point> mousePositions; + //Window* statequeued_; + Window* state_; + Point windowTargetPosition; + float windowOpenState; + + bool running_; + bool break_; + bool FastQuit; + + int lastTick; + int mouseb_; + int mousex_; + int mousey_; + int mousexp_; + int mouseyp_; + int width_; + int height_; + + int maxWidth; + int maxHeight; + }; + +} diff --git a/src/gui/interface/Keys.h b/src/gui/interface/Keys.h new file mode 100644 index 0000000..cd265f9 --- /dev/null +++ b/src/gui/interface/Keys.h @@ -0,0 +1,84 @@ + +#if defined(USE_SDL) +#include "SDL/SDL.h" +#define KEY_UP SDLK_UP +#define KEY_DOWN SDLK_DOWN +#define KEY_RIGHT SDLK_RIGHT +#define KEY_LEFT SDLK_LEFT +#define KEY_HOME SDLK_HOME +#define KEY_END SDLK_END +#define KEY_BACKSPACE SDLK_BACKSPACE +#define KEY_DELETE SDLK_DELETE +#define KEY_TAB SDLK_TAB +#define KEY_RETURN SDLK_RETURN +#define KEY_ENTER SDLK_KP_ENTER +#define KEY_ESCAPE SDLK_ESCAPE + +#define KEY_CTRL SDLK_LCTRL +#define KEY_ALT SDLK_LALT +#define KEY_SHIFT SDLK_LSHIFT + +#define KEY_MOD_NONE KMOD_NONE +#define KEY_MOD_LSHIFT KMOD_LSHIFT +#define KEY_MOD_RSHIFT KMOD_RSHIFT +#define KEY_MOD_LCONTROL KMOD_LCTRL +#define KEY_MOD_RCONTROL KMOD_RCTRL +#define KEY_MOD_LALT KMOD_LALT +#define KEY_MOD_RALT KMOD_RALT +#define KEY_MOD_LMETA KMOD_LMETA +#define KEY_MOD_RMETA KMOD_RMETA +#define KEY_MOD_NUM KMOD_NUM +#define KEY_MOD_CAPS KMOD_CAPS +#define KEY_MOD_MODE KMOD_MODE +#define KEY_MOD_RESERVED KMOD_RESERVED + +#define KEY_MOD_CONTROL KEY_MOD_RCONTROL | KEY_MOD_LCONTROL +#define KEY_MOD_ALT KEY_MOD_RALT | KEY_MOD_LALT +#define KEY_MOD_SHIFT KEY_MOD_RSHIFT | KEY_MOD_LSHIFT + +#define KEY_a SDLK_a +#define KEY_d SDLK_d +#define KEY_s SDLK_s +#define KEY_w SDLK_w + +#define KEY_F1 SDLK_F1 + +#define BUTTON_LEFT SDL_BUTTON_LEFT +#define BUTTON_MIDDLE SDL_BUTTON_MIDDLE +#define BUTTON_RIGHT SDL_BUTTON_RIGHT + +#else + +#define KEY_UP 1 +#define KEY_DOWN 2 +#define KEY_RIGHT 3 +#define KEY_LEFT 4 +#define KEY_HOME 5 +#define KEY_END 6 +#define KEY_BACKSPACE 7 +#define KEY_DELETE 8 +#define KEY_TAB 9 +#define KEY_RETURN 10 +#define KEY_ENTER 11 +#define KEY_ESCAPE 12 + +#define KEY_CTRL 13 +#define KEY_ALT 14 +#define KEY_SHIFT 15 + +#define KEY_MOD_CONTROL 16 +#define KEY_MOD_ALT 17 +#define KEY_MOD_SHIFT 18 + +#define BUTTON_LEFT 19 +#define BUTTON_MIDDLE 20 +#define BUTTON_RIGHT 21 + +#define KEY_a 22 +#define KEY_d 23 +#define KEY_s 24 +#define KEY_w 25 + +#define KEY_F1 26 + +#endif diff --git a/src/gui/interface/Label.cpp b/src/gui/interface/Label.cpp new file mode 100644 index 0000000..24b8035 --- /dev/null +++ b/src/gui/interface/Label.cpp @@ -0,0 +1,420 @@ +#include <string> +#include "Config.h" +#include "Point.h" +#include "Label.h" +#include "Keys.h" +#include "ContextMenu.h" + +using namespace ui; + +Label::Label(Point position, Point size, std::string labelText): + Component(position, size), + text(labelText), + textColour(255, 255, 255), + selectionIndex0(-1), + selectionIndex1(-1), + selectionXL(-1), + selectionXH(-1), + multiline(false), + selecting(false), + autoHeight(size.Y==-1?true:false), + caret(-1) +{ + menu = new ContextMenu(this); + menu->AddItem(ContextMenuItem("Copy", 0, true)); +} + +Label::~Label() +{ + +} + +void Label::SetMultiline(bool status) +{ + multiline = status; + if(status) + { + updateMultiline(); + updateSelection(); + TextPosition(textLines); + } + else + { + TextPosition(text); + } +} + +void Label::SetText(std::string text) +{ + this->text = text; + if(multiline) + { + updateMultiline(); + updateSelection(); + TextPosition(textLines); + } + else + { + TextPosition(text); + } +} + +void Label::AutoHeight() +{ + bool oldAH = autoHeight; + autoHeight = true; + updateMultiline(); + autoHeight = oldAH; +} + +void Label::updateMultiline() +{ + int lines = 1; + if(text.length()>0) + { + char * rawText = new char[text.length()+1]; + std::copy(text.begin(), text.end(), rawText); + rawText[text.length()] = 0; + + char c, pc = 0; + int charIndex = 0; + + int wordWidth = 0; + int lineWidth = 0; + char * wordStart = NULL; + while(c = rawText[charIndex++]) + { + switch(c) + { + case ' ': + lineWidth += Graphics::CharWidth(c); + lineWidth += wordWidth; + wordWidth = 0; + break; + case '\n': + lineWidth = wordWidth = 0; + lines++; + break; + default: + wordWidth += Graphics::CharWidth(c); + break; + } + if(pc == ' ') + { + wordStart = &rawText[charIndex-2]; + } + if ((c != ' ' || pc == ' ') && lineWidth + wordWidth >= Size.X-(Appearance.Margin.Left+Appearance.Margin.Right)) + { + if(wordStart && *wordStart) + { + *wordStart = '\n'; + if (lineWidth != 0) + lineWidth = wordWidth; + } + else if(!wordStart) + { + rawText[charIndex-1] = '\n'; + lineWidth = 0; + } + wordWidth = 0; + wordStart = 0; + lines++; + } + pc = c; + } + if(autoHeight) + { + Size.Y = lines*12; + } + textLines = std::string(rawText); + delete[] rawText; + /*int currentWidth = 0; + char * lastSpace = NULL; + char * currentWord = rawText; + char * nextSpace; + while(true) + { + nextSpace = strchr(currentWord+1, ' '); + if(nextSpace) + nextSpace[0] = 0; + int width = Graphics::textwidth(currentWord); + if(width+currentWidth >= Size.X-(Appearance.Margin.Left+Appearance.Margin.Right)) + { + currentWidth = width; + if(currentWord!=rawText) + { + currentWord[0] = '\n'; + lines++; + } + } + else + currentWidth += width; + if(nextSpace) + nextSpace[0] = ' '; + if(!currentWord[0] || !currentWord[1] || !(currentWord = strchr(currentWord+1, ' '))) + break; + } + if(autoHeight) + { + Size.Y = lines*12; + } + textLines = std::string(rawText); + delete[] rawText;*/ + } + else + { + if(autoHeight) + { + Size.Y = 12; + } + textLines = std::string(""); + } +} + +std::string Label::GetText() +{ + return this->text; +} + +void Label::OnContextMenuAction(int item) +{ + switch(item) + { + case 0: + copySelection(); + break; + } +} + +void Label::OnMouseClick(int x, int y, unsigned button) +{ + if(button == BUTTON_RIGHT) + { + if(menu) + menu->Show(GetScreenPos() + ui::Point(x, y)); + } + else + { + selecting = true; + if(multiline) + selectionIndex0 = Graphics::CharIndexAtPosition((char*)textLines.c_str(), x-textPosition.X, y-textPosition.Y); + else + selectionIndex0 = Graphics::CharIndexAtPosition((char*)text.c_str(), x-textPosition.X, y-textPosition.Y); + selectionIndex1 = selectionIndex0; + + updateSelection(); + } +} + +void Label::copySelection() +{ + std::string currentText = text; + + if(selectionIndex1 > selectionIndex0) { + ClipboardPush((char*)currentText.substr(selectionIndex0, selectionIndex1-selectionIndex0).c_str()); + } else if(selectionIndex0 > selectionIndex1) { + ClipboardPush((char*)currentText.substr(selectionIndex1, selectionIndex0-selectionIndex1).c_str()); + } else { + ClipboardPush((char*)currentText.c_str()); + } +} + +void Label::OnMouseUp(int x, int y, unsigned button) +{ + selecting = false; +} + +void Label::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + if(ctrl && key == 'c') + { + copySelection(); + } +} + +void Label::OnMouseMoved(int localx, int localy, int dx, int dy) +{ + if(selecting) + { + if(multiline) + selectionIndex1 = Graphics::CharIndexAtPosition((char*)textLines.c_str(), localx-textPosition.X, localy-textPosition.Y); + else + selectionIndex1 = Graphics::CharIndexAtPosition((char*)text.c_str(), localx-textPosition.X, localy-textPosition.Y); + updateSelection(); + } +} + +void Label::Tick(float dt) +{ + if(!this->IsFocused() && (selecting || (selectionIndex0 != -1 && selectionIndex1 != -1))) + { + ClearSelection(); + } +} + +int Label::getLowerSelectionBound() +{ + return (selectionIndex0 > selectionIndex1) ? selectionIndex1 : selectionIndex0; +} + +int Label::getHigherSelectionBound() +{ + return (selectionIndex0 > selectionIndex1) ? selectionIndex0 : selectionIndex1; +} + +bool Label::HasSelection() +{ + if(selectionIndex0 != -1 && selectionIndex1 != -1 && selectionIndex0 != selectionIndex1) + return true; + return false; +} + +void Label::ClearSelection() +{ + selecting = false; + selectionIndex0 = -1; + selectionIndex1 = -1; + updateSelection(); +} + +void Label::updateSelection() +{ + std::string currentText; + + if(selectionIndex0 < 0) selectionIndex0 = 0; + if(selectionIndex0 > text.length()) selectionIndex0 = text.length(); + if(selectionIndex1 < 0) selectionIndex1 = 0; + if(selectionIndex1 > text.length()) selectionIndex1 = text.length(); + + if(selectionIndex0 == -1 || selectionIndex1 == -1) + { + selectionXH = -1; + selectionXL = -1; + + textFragments = std::string(currentText); + return; + } + + if(multiline) + currentText = textLines; + else + currentText = text; + + if(selectionIndex1 > selectionIndex0) { + selectionLineH = Graphics::PositionAtCharIndex((char*)currentText.c_str(), selectionIndex1, selectionXH, selectionYH); + selectionLineL = Graphics::PositionAtCharIndex((char*)currentText.c_str(), selectionIndex0, selectionXL, selectionYL); + + textFragments = std::string(currentText); + //textFragments.insert(selectionIndex1, "\x0E"); + //textFragments.insert(selectionIndex0, "\x0F\x01\x01\x01"); + textFragments.insert(selectionIndex1, "\x01"); + textFragments.insert(selectionIndex0, "\x01"); + } else if(selectionIndex0 > selectionIndex1) { + selectionLineH = Graphics::PositionAtCharIndex((char*)currentText.c_str(), selectionIndex0, selectionXH, selectionYH); + selectionLineL = Graphics::PositionAtCharIndex((char*)currentText.c_str(), selectionIndex1, selectionXL, selectionYL); + + textFragments = std::string(currentText); + //textFragments.insert(selectionIndex0, "\x0E"); + //textFragments.insert(selectionIndex1, "\x0F\x01\x01\x01"); + textFragments.insert(selectionIndex0, "\x01"); + textFragments.insert(selectionIndex1, "\x01"); + } else { + selectionXH = -1; + selectionXL = -1; + + textFragments = std::string(currentText); + } + + if(displayText.length()) + { + displayText = tDisplayText; + if(selectionIndex1 > selectionIndex0) { + int tSelectionIndex1 = Graphics::CharIndexAtPosition((char*)displayText.c_str(), selectionXH, selectionYH); + int tSelectionIndex0 = Graphics::CharIndexAtPosition((char*)displayText.c_str(), selectionXL, selectionYL); + + displayText.insert(tSelectionIndex1, "\x01"); + displayText.insert(tSelectionIndex0, "\x01"); + } else if(selectionIndex0 > selectionIndex1) { + int tSelectionIndex0 = Graphics::CharIndexAtPosition((char*)displayText.c_str(), selectionXH, selectionYH); + int tSelectionIndex1 = Graphics::CharIndexAtPosition((char*)displayText.c_str(), selectionXL, selectionYL); + + displayText.insert(tSelectionIndex0, "\x01"); + displayText.insert(tSelectionIndex1, "\x01"); + } + } +} + +void Label::SetDisplayText(std::string newText) +{ + displayText = newText; + tDisplayText = displayText; +} + +void Label::Draw(const Point& screenPos) +{ + if(!drawn) + { + if(multiline) + { + TextPosition(textLines); + updateMultiline(); + updateSelection(); + } + else + TextPosition(text); + drawn = true; + } + Graphics * g = Engine::Ref().g; + + std::string cDisplayText = displayText; + + if(!cDisplayText.length()) + { + if(selectionXL != -1 && selectionXH != -1) + { + cDisplayText = textFragments; + } + else + { + if(multiline) + cDisplayText = textLines; + else + cDisplayText = text; + } + } + + if(multiline) + { + if(selectionXL != -1 && selectionXH != -1) + { + if(selectionLineH - selectionLineL > 0) + { + g->fillrect(screenPos.X+textPosition.X+selectionXL, (screenPos.Y+textPosition.Y-1)+selectionYL, textSize.X-(selectionXL), 10, 255, 255, 255, 255); + for(int i = 1; i < selectionLineH-selectionLineL; i++) + { + g->fillrect(screenPos.X+textPosition.X, (screenPos.Y+textPosition.Y-1)+selectionYL+(i*12), textSize.X, 10, 255, 255, 255, 255); + } + g->fillrect(screenPos.X+textPosition.X, (screenPos.Y+textPosition.Y-1)+selectionYH, selectionXH, 10, 255, 255, 255, 255); + + } else { + g->fillrect(screenPos.X+textPosition.X+selectionXL, screenPos.Y+selectionYL+textPosition.Y-1, selectionXH-(selectionXL), 10, 255, 255, 255, 255); + } + g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, cDisplayText, textColour.Red, textColour.Green, textColour.Blue, 255); + } + else + { + g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, cDisplayText, textColour.Red, textColour.Green, textColour.Blue, 255); + } + } else { + if(selectionXL != -1 && selectionXH != -1) + { + g->fillrect(screenPos.X+textPosition.X+selectionXL, screenPos.Y+textPosition.Y-1, selectionXH-(selectionXL), 10, 255, 255, 255, 255); + g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, cDisplayText, textColour.Red, textColour.Green, textColour.Blue, 255); + } + else + { + g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, cDisplayText, textColour.Red, textColour.Green, textColour.Blue, 255); + } + } +} + diff --git a/src/gui/interface/Label.h b/src/gui/interface/Label.h new file mode 100644 index 0000000..d4e5088 --- /dev/null +++ b/src/gui/interface/Label.h @@ -0,0 +1,72 @@ +#ifndef LABEL_H +#define LABEL_H + +#include <string> + +#include "Component.h" +#include "PowderToy.h" +#include "Colour.h" + +namespace ui +{ + class Label : public Component + { + protected: + std::string textFragments; + std::string textLines; + std::string displayText; + std::string tDisplayText; + + std::string text; + Colour textColour; + int caret; + int selectionIndex0; + int selectionIndex1; + + int selectionXL; + int selectionXH; + int selectionYL; + int selectionYH; + int selectionLineL; + int selectionLineH; + + bool multiline; + bool selecting; + bool autoHeight; + + void updateMultiline(); + void updateSelection(); + + int getLowerSelectionBound(); + int getHigherSelectionBound(); + + virtual void copySelection(); + public: + //Label(Window* parent_state, std::string labelText); + Label(Point position, Point size, std::string labelText); + //Label(std::string labelText); + virtual ~Label(); + + virtual void SetMultiline(bool status); + + virtual void SetText(std::string text); + virtual void SetDisplayText(std::string newText); + virtual std::string GetText(); + + virtual bool HasSelection(); + virtual void ClearSelection(); + virtual void AutoHeight(); + + void SetTextColour(Colour textColour) { this->textColour = textColour; } + + virtual void OnContextMenuAction(int item); + virtual void OnMouseClick(int x, int y, unsigned button); + virtual void OnMouseUp(int x, int y, unsigned button); + virtual void OnMouseMoved(int localx, int localy, int dx, int dy); + virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + virtual void Draw(const Point& screenPos); + virtual void Tick(float dt); + }; +} + +#endif // LABEL_H diff --git a/src/gui/interface/LuaProgressBar.h b/src/gui/interface/LuaProgressBar.h new file mode 100644 index 0000000..dc2ef4e --- /dev/null +++ b/src/gui/interface/LuaProgressBar.h @@ -0,0 +1,30 @@ +#pragma once + +extern "C" { + #include "lua.h" + #include "lauxlib.h" + #include "lualib.h" +} + +#include "LuaLuna.h" +#include "LuaComponent.h" + +namespace ui +{ + class ProgressBar; +} + +class LuaScriptInterface; + +class LuaProgressBar: public LuaComponent +{ + ui::ProgressBar * progressBar; + int onValueChangedFunction; + int value(lua_State * l); +public: + static const char className[]; + static Luna<LuaProgressBar>::RegType methods[]; + + LuaProgressBar(lua_State * l); + ~LuaProgressBar(); +};
\ No newline at end of file diff --git a/src/gui/interface/Panel.cpp b/src/gui/interface/Panel.cpp new file mode 100644 index 0000000..3b19264 --- /dev/null +++ b/src/gui/interface/Panel.cpp @@ -0,0 +1,457 @@ +#pragma once +#include <vector> +//#include "Platform.h" + +#include "gui/interface/Panel.h" + +#include "gui/interface/Point.h" +#include "gui/interface/Window.h" +#include "gui/interface/Component.h" +#include "graphics/Graphics.h" + +using namespace ui; + +Panel::Panel(Point position, Point size): + Component(position, size), + InnerSize(size), + ViewportPosition(0, 0), + mouseInside(false) +{ +#ifdef OGLI + GLint lastVid; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &lastVid); + + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &myVidTex); + glBindTexture(GL_TEXTURE_2D, myVidTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, XRES+BARSIZE, YRES+MENUSIZE, 0, GL_RGBA, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + + //FBO + glGenFramebuffers(1, &myVid); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, myVid); + glEnable(GL_BLEND); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, myVidTex, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // Reset framebuffer binding + glDisable(GL_TEXTURE_2D); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, lastVid); +#else + myVid = new pixel[(XRES+BARSIZE)*(YRES+MENUSIZE)]; +#endif +} + +Panel::~Panel() +{ + for(unsigned i = 0; i < children.size(); ++i) + { + if( children[i] ) + delete children[i]; + } +#ifdef OGLI + glDeleteTextures(1, &myVidTex); + glDeleteFramebuffers(1, &myVid); +#else + delete[] myVid; +#endif +} + +void Panel::AddChild(Component* c) +{ + c->SetParent(this); + c->SetParentWindow(this->GetParentWindow()); +} + +int Panel::GetChildCount() +{ + return children.size(); +} + +Component* Panel::GetChild(unsigned idx) +{ + return children[idx]; +} + +void Panel::RemoveChild(Component* c) +{ + for(int i = 0; i < children.size(); ++i) + { + if(children[i] == c) + { + //remove child from parent. Does not free memory + children.erase(children.begin() + i); + if (this->GetParentWindow()->IsFocused(c)) + this->GetParentWindow()->FocusComponent(NULL); + break; + } + } +} + +void Panel::RemoveChild(unsigned idx, bool freeMem) +{ + if(freeMem) + delete children[idx]; + + children.erase(children.begin() + idx); +} + +void Panel::Draw(const Point& screenPos) +{ + + // draw ourself first + XDraw(screenPos); +#ifdef OGLI + GLint lastVid; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &lastVid); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, myVid); + glClearColor(1.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); +#else + pixel * lastVid = ui::Engine::Ref().g->vid; + ui::Engine::Ref().g->vid = myVid; + std::fill(myVid, myVid+((XRES+BARSIZE)*(YRES+MENUSIZE)), 0); +#endif + + // attempt to draw all children + for(int i = 0; i < children.size(); ++i) + { + // the component must be visible + if(children[i]->Visible) + { + //check if the component is in the screen, draw if it is + if( children[i]->Position.X + ViewportPosition.X + children[i]->Size.X >= 0 && + children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y >= 0 && + children[i]->Position.X + ViewportPosition.X < ui::Engine::Ref().GetWidth() && + children[i]->Position.Y + ViewportPosition.Y < ui::Engine::Ref().GetHeight() ) + { + Point scrpos = /*screenPos + */children[i]->Position + ViewportPosition; + children[i]->Draw(scrpos); + } + } + } + +#ifdef OGLI + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, lastVid); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, myVidTex); + + int x = screenPos.X, y = screenPos.Y; + int h = Size.Y, w = Size.X; + + double texX = double(Size.X)/double(XRES+BARSIZE), texY = 1, texYB = 1-(double(Size.Y)/double(YRES+MENUSIZE)); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); + glTexCoord2d(0, texYB); + glVertex2f(x, y+h); + glTexCoord2d(texX, texYB); + glVertex2f(x+w, y+h); + glTexCoord2d(texX, texY); + glVertex2f(x+w, y); + glTexCoord2d(0, texY); + glVertex2f(x, y); + glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); +#else + ui::Engine::Ref().g->vid = lastVid; + + //dst=(pixel *)sdl_scrn->pixels+y*sdl_scrn->pitch/PIXELSIZE+x; + for (int row = 0; row < Size.Y; row++) + { + std::copy(myVid+(row*(XRES+BARSIZE)), myVid+(row*(XRES+BARSIZE))+Size.X, lastVid+((screenPos.Y+row)*(XRES+BARSIZE))+screenPos.X); + } +#endif +} + +void Panel::Tick(float dt) +{ + // tick ourself first + XTick(dt); + + // tick our children + for(unsigned i = 0; i < children.size(); ++i) + children[i]->Tick(dt); +} + +void Panel::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + XOnKeyPress(key, character, shift, ctrl, alt); +} + +void Panel::OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + XOnKeyRelease(key, character, shift, ctrl, alt); +} + +void Panel::OnMouseClick(int localx, int localy, unsigned button) +{ + bool childclicked = false; + + //check if clicked a child + for(int i = children.size()-1; i >= 0 ; --i) + { + //child must be unlocked + if(!children[i]->Locked) + { + //is mouse inside? + if( localx >= children[i]->Position.X + ViewportPosition.X && + localy >= children[i]->Position.Y + ViewportPosition.Y && + localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X && + localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y ) + { + childclicked = true; + GetParentWindow()->FocusComponent(children[i]); + children[i]->OnMouseClick(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, button); + break; + } + } + } + + //if a child wasn't clicked, send click to ourself + if(!childclicked) + { + XOnMouseClick(localx, localy, button); + GetParentWindow()->FocusComponent(this); + } +} + +void Panel::OnMouseDown(int x, int y, unsigned button) +{ + XOnMouseDown(x, y, button); + for(int i = 0; i < children.size(); ++i) + { + if(!children[i]->Locked) + children[i]->OnMouseDown(x, y, button); + } +} + +void Panel::OnMouseHover(int localx, int localy) +{ + // check if hovering on children + for(int i = children.size() - 1; i >= 0; --i) + { + if(!children[i]->Locked) + { + if( localx >= children[i]->Position.X && + localy >= children[i]->Position.Y && + localx < children[i]->Position.X + children[i]->Size.X && + localy < children[i]->Position.Y + children[i]->Size.Y ) + { + children[i]->OnMouseHover(localx - children[i]->Position.X, localy - children[i]->Position.Y); + break; + } + } + } + + // always allow hover on parent (?) + XOnMouseHover(localx, localy); +} + +void Panel::OnMouseMoved(int localx, int localy, int dx, int dy) +{ + XOnMouseMoved(localx, localy, dx, dy); + for(int i = 0; i < children.size(); ++i) + { + if(!children[i]->Locked) + children[i]->OnMouseMoved(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, dx, dy); + } +} + +void Panel::OnMouseMovedInside(int localx, int localy, int dx, int dy) +{ + mouseInside = true; + for(int i = 0; i < children.size(); ++i) + { + if(!children[i]->Locked) + { + Point local (localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y) + , prevlocal (local.X - dx, local.Y - dy); + + // mouse currently inside? + if( local.X >= 0 && + local.Y >= 0 && + local.X < children[i]->Size.X && + local.Y < children[i]->Size.Y ) + { + children[i]->OnMouseMovedInside(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, dx, dy); + + // was the mouse outside? + if(!(prevlocal.X >= 0 && + prevlocal.Y >= 0 && + prevlocal.X < children[i]->Size.X && + prevlocal.Y < children[i]->Size.Y ) ) + { + children[i]->OnMouseEnter(local.X, local.Y); + } + } + // if not currently inside + else + { + // was the mouse inside? + if( prevlocal.X >= 0 && + prevlocal.Y >= 0 && + prevlocal.X < children[i]->Size.X && + prevlocal.Y < children[i]->Size.Y ) + { + children[i]->OnMouseLeave(local.X, local.Y); + } + + } + } + } + + // always allow hover on parent (?) + XOnMouseMovedInside(localx, localy, dx, dy); +} + +void Panel::OnMouseEnter(int localx, int localy) +{ + mouseInside = true; + XOnMouseEnter(localx, localy); +} + +void Panel::OnMouseLeave(int localx, int localy) +{ + mouseInside = false; + XOnMouseLeave(localx, localy); +} + +void Panel::OnMouseUnclick(int localx, int localy, unsigned button) +{ + bool childunclicked = false; + + //check if clicked a child + for(int i = children.size()-1; i >= 0 ; --i) + { + //child must be unlocked + if(!children[i]->Locked) + { + //is mouse inside? + if( localx >= children[i]->Position.X + ViewportPosition.X && + localy >= children[i]->Position.Y + ViewportPosition.Y && + localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X && + localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y ) + { + childunclicked = true; + children[i]->OnMouseUnclick(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, button); + break; + } + } + } + + //if a child wasn't clicked, send click to ourself + if(!childunclicked) + { + XOnMouseUnclick(localx, localy, button); + } +} + +void Panel::OnMouseUp(int x, int y, unsigned button) +{ + XOnMouseUp(x, y, button); + for(int i = 0; i < children.size(); ++i) + { + if(!children[i]->Locked) + children[i]->OnMouseUp(x, y, button); + } +} + +void Panel::OnMouseWheel(int localx, int localy, int d) +{ + XOnMouseWheel(localx, localy, d); + for(int i = 0; i < children.size(); ++i) + { + if(!children[i]->Locked) + children[i]->OnMouseWheel(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, d); + } +} + +void Panel::OnMouseWheelInside(int localx, int localy, int d) +{ + XOnMouseWheelInside(localx, localy, d); + //check if clicked a child + for(int i = children.size()-1; i >= 0 ; --i) + { + //child must be unlocked + if(!children[i]->Locked) + { + //is mouse inside? + if( localx >= children[i]->Position.X + ViewportPosition.X && + localy >= children[i]->Position.Y + ViewportPosition.Y && + localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X && + localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y ) + { + children[i]->OnMouseWheelInside(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, d); + break; + } + } + } +} + +// ***** OVERRIDEABLES ***** +// Kept empty. + +void Panel::XDraw(const Point& screenPos) +{ +} + +void Panel::XTick(float dt) +{ +} + +void Panel::XOnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ +} + +void Panel::XOnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ +} + +void Panel::XOnMouseClick(int localx, int localy, unsigned button) +{ +} + +void Panel::XOnMouseDown(int x, int y, unsigned button) +{ +} + +void Panel::XOnMouseHover(int localx, int localy) +{ +} + +void Panel::XOnMouseMoved(int localx, int localy, int dx, int dy) +{ +} + +void Panel::XOnMouseMovedInside(int localx, int localy, int dx, int dy) +{ +} + +void Panel::XOnMouseEnter(int localx, int localy) +{ +} + +void Panel::XOnMouseLeave(int localx, int localy) +{ +} + +void Panel::XOnMouseUnclick(int localx, int localy, unsigned button) +{ +} + +void Panel::XOnMouseUp(int x, int y, unsigned button) +{ +} + +void Panel::XOnMouseWheel(int localx, int localy, int d) +{ +} + +void Panel::XOnMouseWheelInside(int localx, int localy, int d) +{ +} diff --git a/src/gui/interface/Panel.h b/src/gui/interface/Panel.h new file mode 100644 index 0000000..119be5d --- /dev/null +++ b/src/gui/interface/Panel.h @@ -0,0 +1,150 @@ +#pragma once +#include <vector> +//#include "Platform.h" + +#include "gui/interface/Point.h" +#include "gui/interface/Window.h" +#include "gui/interface/Component.h" + +#ifdef OGLI +#include "graphics/OpenGLHeaders.h" +#endif + + +class Graphics; +namespace ui +{ + /* class XComponent + * + * An eXtension of the Component class. + * Adds the ability to have child components. + * + * See sys::Component + */ + +class Component; + class Panel : public Component + { + public: + friend class Component; + +#ifdef OGLI + GLuint myVid, myVidTex; +#else + pixel * myVid; +#endif + ui::Point InnerSize; + ui::Point ViewportPosition; + + Panel(Point position, Point size); + virtual ~Panel(); + + /* Add a child component. + * Similar to XComponent::SetParent + * + * If the component is already parented, then this will become the new parent. + */ + void AddChild(Component* c); + + // Remove child from component. This DOES NOT free the component from memory. + void RemoveChild(Component* c); + + // Remove child from component. This WILL free the component from memory unless told otherwise. + void RemoveChild(unsigned idx, bool freeMem = true); + + //Grab the number of children this component owns. + int GetChildCount(); + + //Get child of this component by index. + Component* GetChild(unsigned idx); + + void Tick(float dt); + void Draw(const Point& screenPos); + + void OnMouseHover(int localx, int localy); + void OnMouseMoved(int localx, int localy, int dx, int dy); + void OnMouseMovedInside(int localx, int localy, int dx, int dy); + void OnMouseEnter(int localx, int localy); + void OnMouseLeave(int localx, int localy); + void OnMouseDown(int x, int y, unsigned button); + void OnMouseUp(int x, int y, unsigned button); + void OnMouseClick(int localx, int localy, unsigned button); + void OnMouseUnclick(int localx, int localy, unsigned button); + void OnMouseWheel(int localx, int localy, int d); + void OnMouseWheelInside(int localx, int localy, int d); + void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + void OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt); + + protected: + // child components + std::vector<ui::Component*> children; + bool mouseInside; + + //UI functions: + /* + void XTick(float dt); + void XDraw(const Point& screenPos); + + void XOnMouseHover(int localx, int localy); + void XOnMouseMoved(int localx, int localy, int dx, int dy); + void XOnMouseMovedInside(int localx, int localy, int dx, int dy); + void XOnMouseEnter(int localx, int localy); + void XOnMouseLeave(int localx, int localy); + void XOnMouseDown(int x, int y, unsigned int button); + void XOnMouseUp(int x, int y, unsigned int button); + void XOnMouseClick(int localx, int localy, unsigned int button); + void XOnMouseUnclick(int localx, int localy, unsigned int button); + void XOnMouseWheel(int localx, int localy, int d); + void XOnMouseWheelInside(int localx, int localy, int d); + void XOnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + void XOnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt); + */ + + // Overridable. Called by XComponent::Tick() + virtual void XTick(float dt); + + // Overridable. Called by XComponent::Draw() + virtual void XDraw(const Point& screenPos); + + + // Overridable. Called by XComponent::XOnMouseHover() + virtual void XOnMouseHover(int localx, int localy); + + // Overridable. Called by XComponent::OnMouseMoved() + virtual void XOnMouseMoved(int localx, int localy, int dx, int dy); + + // Overridable. Called by XComponent::OnMouseMovedInside() + virtual void XOnMouseMovedInside(int localx, int localy, int dx, int dy); + + // Overridable. Called by XComponent::OnMouseEnter() + virtual void XOnMouseEnter(int localx, int localy); + + // Overridable. Called by XComponent::OnMouseLeave() + virtual void XOnMouseLeave(int localx, int localy); + + // Overridable. Called by XComponent::OnMouseDown() + virtual void XOnMouseDown(int x, int y, unsigned button); + + // Overridable. Called by XComponent::OnMouseUp() + virtual void XOnMouseUp(int x, int y, unsigned button); + + // Overridable. Called by XComponent::OnMouseClick() + virtual void XOnMouseClick(int localx, int localy, unsigned button); + + // Overridable. Called by XComponent::OnMouseUnclick() + virtual void XOnMouseUnclick(int localx, int localy, unsigned button); + + // Overridable. Called by XComponent::OnMouseWheel() + virtual void XOnMouseWheel(int localx, int localy, int d); + + // Overridable. Called by XComponent::OnMouseWheelInside() + virtual void XOnMouseWheelInside(int localx, int localy, int d); + + // Overridable. Called by XComponent::OnKeyPress() + virtual void XOnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + + // Overridable. Called by XComponent::OnKeyRelease() + virtual void XOnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt); + }; + +} diff --git a/src/gui/interface/Platform.h b/src/gui/interface/Platform.h new file mode 100644 index 0000000..5940303 --- /dev/null +++ b/src/gui/interface/Platform.h @@ -0,0 +1,71 @@ +#pragma once + +typedef unsigned short Uint16; + +/* ***** Primitive Types ***** */ + +#ifndef NULL +# define NULL 0 +#endif + +#include <climits> +namespace sys +{ + +#if UCHAR_MAX == 0xFF //char + typedef signed char s8; + typedef unsigned char u8; +#else +# error No 8-Bit Integer supported. +#endif +#if USHRT_MAX == 0xFFFF //short + typedef signed short s16; + typedef unsigned short u16; +#elif UINT_MAX == 0xFFFF + typedef signed int s16; + typedef unsigned int u16; +#elif ULONG_MAX == 0xFFFF + typedef signed long s16; + typedef unsigned long u16; + #else + # error No 16-Bit Integer supported. + #endif + #if USHRT_MAX == 0xFFFFFFFF //int + typedef signed short s32; + typedef unsigned short u32; +#elif UINT_MAX == 0xFFFFFFFF + typedef signed int s32; + typedef unsigned int u32; +#elif ULONG_MAX == 0xFFFFFFFF + typedef signed long s32; + typedef unsigned long u32; + #else + # error No 32-Bit Integer supported. + #endif +#if UINT_MAX == 0xFFFFFFFFFFFFFFFF //long + typedef signed int s64; + typedef unsigned int u64; +#elif ULONG_MAX == 0xFFFFFFFFFFFFFFFF + typedef signed long s64; + typedef unsigned long u64; +#elif ULLONG_MAX == 0xFFFFFFFFFFFFFFFF + typedef signed long long s64; + typedef unsigned long long u64; +#else +# pragma message("Warning: 64-bit not supported. s64 and u64 defined as 32-bit.") + typedef s32 s64; + typedef u32 u64; +#endif +//floating +typedef float f32; +typedef double f64; +//misc +typedef u8 byte; +typedef u8 ubyte; +typedef s8 sbyte; +typedef s64 llong; +typedef s64 sllong; +typedef u64 ullong; +typedef char* cstring; + +} //namespace sys diff --git a/src/gui/interface/Point.h b/src/gui/interface/Point.h new file mode 100644 index 0000000..d2eff6b --- /dev/null +++ b/src/gui/interface/Point.h @@ -0,0 +1,146 @@ +#pragma once +#include "Platform.h" + +namespace ui +{ + +//Lightweight 2D Int32/Float32 Point struct for UI +struct Point +{ +#if ENABLE_FLOAT_UI +# define POINT_T float +#else +# define POINT_T int +#endif + + POINT_T X; + POINT_T Y; + + Point(POINT_T x, POINT_T y) + : X(x) + , Y(y) + { + } + + inline Point operator - () const + { + return Point(-X, -Y); + } + + inline Point operator + (const Point& v) const + { + return Point(X + v.X, Y + v.Y); + } + + inline Point operator + (const int v) const + { + return Point(X + v, Y + v); + } + + inline Point operator - (const Point& v) const + { + return Point(X - v.X, Y - v.Y); + } + + inline Point operator - (const int v) const + { + return Point(X - v, Y - v); + } + + inline Point operator * (const Point& v) const + { + return Point(X * v.X, Y * v.Y); + } + + inline Point operator * (int v) const + { + return Point(X * static_cast<POINT_T>(v), Y * static_cast<POINT_T>(v)); + } + + inline Point operator * (float v) const + { + return Point(X * static_cast<POINT_T>(v), Y * static_cast<POINT_T>(v)); + } + + inline Point operator / (const Point& v) const + { + return Point(X / v.X, Y / v.Y); + } + + inline Point operator / (int v) const + { + return Point(X / static_cast<POINT_T>(v), Y / static_cast<POINT_T>(v)); + } + + inline Point operator / (float v) const + { + return Point(X / static_cast<POINT_T>(v), Y / static_cast<POINT_T>(v)); + } + + inline void operator += (const Point& v) + { + X += v.X; + Y += v.Y; + } + + inline void operator -= (const Point& v) + { + X -= v.X; + Y -= v.Y; + } + + inline void operator *= (const Point& v) + { + X *= v.X; + Y *= v.Y; + } + + inline void operator *= (int v) + { + X *= static_cast<POINT_T>(v); + Y *= static_cast<POINT_T>(v); + } + + inline void operator *= (float v) + { + X *= static_cast<POINT_T>(v); + Y *= static_cast<POINT_T>(v); + } + + inline void operator /= (const Point& v) + { + X /= v.X; + Y /= v.Y; + } + + inline void operator /= (int v) + { + X /= static_cast<POINT_T>(v); + Y /= static_cast<POINT_T>(v); + } + + inline void operator /= (float v) + { + X /= static_cast<POINT_T>(v); + Y /= static_cast<POINT_T>(v); + } + + inline bool operator == (const Point& v) const + { + return (X == v.X && Y == v.Y); + } + + inline bool operator != (const Point& v) const + { + return (X != v.X || Y != v.Y); + } + + inline void operator = (const Point& v) + { + X = v.X; + Y = v.Y; + } + +}; + +} diff --git a/src/gui/interface/ProgressBar.cpp b/src/gui/interface/ProgressBar.cpp new file mode 100644 index 0000000..95da72c --- /dev/null +++ b/src/gui/interface/ProgressBar.cpp @@ -0,0 +1,81 @@ +#include "ProgressBar.h" +#include "gui/Style.h" + +using namespace ui; + +ProgressBar::ProgressBar(Point position, Point size, int startProgress, std::string startStatus): + Component(position, size), + intermediatePos(0.0f), + progressStatus(""), + progress(0) +{ + SetStatus(startStatus); + SetProgress(startProgress); +} + +void ProgressBar::SetProgress(int progress) +{ + this->progress = progress; + if(this->progress > 100) + this->progress = 100; +} + +int ProgressBar::GetProgress() +{ + return progress; +} + +void ProgressBar::SetStatus(std::string status) +{ + progressStatus = status; +} + +std::string ProgressBar::GetStatus() +{ + return progressStatus; +} + +void ProgressBar::Draw(const Point & screenPos) +{ + Graphics * g = ui::Engine::Ref().g; + + ui::Colour progressBarColour = style::Colour::WarningTitle; + + g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 255, 255, 255, 255); + + if(progress!=-1) + { + if(progress > 0) + { + if(progress > 100) + progress = 100; + float size = float(Size.X-4)*(float(progress)/100.0f); // TIL... + size = std::min(std::max(size, 0.0f), float(Size.X-4)); + g->fillrect(screenPos.X + 2, screenPos.Y + 2, size, Size.Y-4, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255); + } + } else { + int size = 40, rsize = 0; + float position = float(Size.X-4)*(intermediatePos/100.0f); + if(position + size - 1 > Size.X-4) + { + size = (Size.X-4)-position+1; + rsize = 40-size; + } + g->fillrect(screenPos.X + 2 + position, screenPos.Y + 2, size, Size.Y-4, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255); + if(rsize) + { + g->fillrect(screenPos.X + 2, screenPos.Y + 2, rsize, Size.Y-4, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255); + } + } + if(progress<50) + g->drawtext(screenPos.X + ((Size.X-Graphics::textwidth(progressStatus.c_str()))/2), screenPos.Y + (Size.Y-8)/2, progressStatus, 255, 255, 255, 255); + else + g->drawtext(screenPos.X + ((Size.X-Graphics::textwidth(progressStatus.c_str()))/2), screenPos.Y + (Size.Y-8)/2, progressStatus, 0, 0, 0, 255); +} + +void ProgressBar::Tick(float dt) +{ + intermediatePos += 1.0f*dt; + if(intermediatePos>100.0f) + intermediatePos = 0.0f; +}
\ No newline at end of file diff --git a/src/gui/interface/ProgressBar.h b/src/gui/interface/ProgressBar.h new file mode 100644 index 0000000..fc47f0c --- /dev/null +++ b/src/gui/interface/ProgressBar.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Component.h" + +namespace ui +{ + class ProgressBar: public Component + { + int progress; + float intermediatePos; + std::string progressStatus; + public: + ProgressBar(Point position, Point size, int startProgress = 0, std::string startStatus = ""); + virtual void SetProgress(int progress); + virtual int GetProgress(); + virtual void SetStatus(std::string status); + virtual std::string GetStatus(); + virtual void Draw(const Point & screenPos); + virtual void Tick(float dt); + }; +} diff --git a/src/gui/interface/RichLabel.cpp b/src/gui/interface/RichLabel.cpp new file mode 100644 index 0000000..f6143d3 --- /dev/null +++ b/src/gui/interface/RichLabel.cpp @@ -0,0 +1,198 @@ +#include <vector> +#include <exception> + +#include "RichLabel.h" +#include "Misc.h" +#include "gui/interface/Point.h" +#include "gui/interface/Component.h" +#include "graphics/Graphics.h" + +using namespace ui; + +struct RichTextParseException: public std::exception { + std::string message; +public: + RichTextParseException(std::string message_ = "Parse error"): message(message_) {} + const char * what() const throw() + { + return message.c_str(); + } + ~RichTextParseException() throw() {}; +}; + +RichLabel::RichLabel(Point position, Point size, std::string labelText): + Component(position, size), + textSource(labelText), + displayText("") +{ + updateRichText(); +} + +RichLabel::~RichLabel() +{ + +} + +void RichLabel::updateRichText() +{ + regions.clear(); + displayText = ""; + + if(textSource.length()) + { + + enum State { ReadText, ReadData, ReadRegion, ReadDataStart }; + State state = ReadText; + + int currentDataPos = 0; + char * currentData = new char[textSource.length()+1]; + std::fill(currentData, currentData+textSource.length()+1, 0); + + int finalTextPos = 0; + char * finalText = new char[textSource.length()+1]; + std::fill(finalText, finalText+textSource.length()+1, 0); + + int originalTextPos = 0; + char * originalText = new char[textSource.length()+1]; + std::copy(textSource.begin(), textSource.end(), originalText); + originalText[textSource.length()] = 0; + + int stackPos = -1; + RichTextRegion * regionsStack = new RichTextRegion[256]; + + try + { + while(originalText[originalTextPos]) + { + char current = originalText[originalTextPos]; + + if(state == ReadText) + { + if(current == '{') + { + if(stackPos > 255) + throw RichTextParseException("Too many nested regions"); + stackPos++; + regionsStack[stackPos].start = finalTextPos; + regionsStack[stackPos].finish = finalTextPos; + state = ReadRegion; + } + else if(current == '}') + { + if(stackPos >= 0) + { + currentData[currentDataPos] = 0; + regionsStack[stackPos].actionData = std::string(currentData); + regions.push_back(regionsStack[stackPos]); + stackPos--; + } + else + { + throw RichTextParseException("Unexpected '}'"); + } + } + else + { + finalText[finalTextPos++] = current; + finalText[finalTextPos] = 0; + if(stackPos >= 0) + { + regionsStack[stackPos].finish = finalTextPos; + } + } + } + else if(state == ReadData) + { + if(current == '|') + { + state = ReadText; + } + else + { + currentData[currentDataPos++] = current; + currentData[currentDataPos] = 0; + } + } + else if(state == ReadDataStart) + { + if(current != ':') + { + throw RichTextParseException("Expected ':'"); + } + state = ReadData; + currentDataPos = 0; + } + else if(state == ReadRegion) + { + if(stackPos >= 0) + { + regionsStack[stackPos].action = current; + state = ReadDataStart; + } + else + { + throw RichTextParseException(); + } + } + + originalTextPos++; + } + + if(stackPos != -1) + throw RichTextParseException("Unclosed region"); + + finalText[finalTextPos] = 0; + displayText = std::string(finalText); + } + catch (const RichTextParseException & e) + { + displayText = "\br[Parse exception: " + std::string(e.what()) + "]"; + regions.clear(); + } + delete[] currentData; + delete[] finalText; + delete[] originalText; + delete[] regionsStack; + } + TextPosition(displayText); +} + +void RichLabel::SetText(std::string text) +{ + textSource = text; + updateRichText(); +} + +std::string RichLabel::GetDisplayText() +{ + return displayText; +} + +std::string RichLabel::GetText() +{ + return textSource; +} + +void RichLabel::Draw(const Point& screenPos) +{ + Graphics * g = ui::Engine::Ref().g; + ui::Colour textColour = Appearance.TextInactive; + g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, displayText, textColour.Red, textColour.Green, textColour.Blue, 255); +} + +void RichLabel::OnMouseClick(int x, int y, unsigned button) +{ + int cursorPosition = Graphics::CharIndexAtPosition((char*)displayText.c_str(), x-textPosition.X, y-textPosition.Y); + for(std::vector<RichTextRegion>::iterator iter = regions.begin(), end = regions.end(); iter != end; ++iter) + { + if((*iter).start <= cursorPosition && (*iter).finish >= cursorPosition) + { + switch((*iter).action) + { + case 'a': + OpenURI((*iter).actionData); + break; + } + } + } +} diff --git a/src/gui/interface/RichLabel.h b/src/gui/interface/RichLabel.h new file mode 100644 index 0000000..d9682f2 --- /dev/null +++ b/src/gui/interface/RichLabel.h @@ -0,0 +1,39 @@ +#pragma once + +#include <string> + +#include "Component.h" +#include "Colour.h" + +namespace ui +{ + class RichLabel : public Component + { + public: + struct RichTextRegion + { + int start; + int finish; + int action; + std::string actionData; + }; + + RichLabel(Point position, Point size, std::string richText); + + virtual ~RichLabel(); + + virtual void SetText(std::string text); + virtual std::string GetDisplayText(); + virtual std::string GetText(); + + virtual void Draw(const Point& screenPos); + virtual void OnMouseClick(int x, int y, unsigned button); + protected: + std::string textSource; + std::string displayText; + + std::vector<RichTextRegion> regions; + + void updateRichText(); + }; +} diff --git a/src/gui/interface/SaveButton.cpp b/src/gui/interface/SaveButton.cpp new file mode 100644 index 0000000..9b2950f --- /dev/null +++ b/src/gui/interface/SaveButton.cpp @@ -0,0 +1,430 @@ +#include <iostream> +#include <typeinfo> + +#include "SaveButton.h" +#include "client/SaveInfo.h" +#include "graphics/Graphics.h" +#include "Engine.h" +#include "client/requestbroker/RequestBroker.h" +#include "simulation/SaveRenderer.h" +#include "Format.h" +#include "ContextMenu.h" +#include "Keys.h" + +namespace ui { + +SaveButton::SaveButton(Point position, Point size, SaveInfo * save): + Component(position, size), + file(NULL), + save(save), + thumbnail(NULL), + isMouseInside(false), + isButtonDown(false), + actionCallback(NULL), + selectable(false), + selected(false), + waitingForThumb(false), + isMouseInsideAuthor(false), + isMouseInsideHistory(false), + showVotes(false) +{ + if(save) + { + name = save->name; + if(Graphics::textwidth((char *)name.c_str()) > Size.X) + { + int position = Graphics::textwidthx((char *)name.c_str(), Size.X - 22); + name = name.erase(position, name.length()-position); + name += "..."; + } + + std::string votes, icon; + + votes = format::NumberToString<int>(save->GetVotesUp()-save->GetVotesDown()); + icon += 0xBB; + for (int j = 1; j < votes.length(); j++) + icon += 0xBC; + icon += 0xB9; + icon += 0xBA; + + votesBackground = icon; + + for (std::string::iterator iter = icon.begin(), end = icon.end(); iter != end; ++iter) + *iter -= 14; + + votesBackground2 = icon; + + for (std::string::iterator iter = votes.begin(), end = votes.end(); iter != end; ++iter) + if(*iter != '-') + *iter += 127; + + votesString = votes; + + int voteMax = std::max(save->GetVotesUp(),save->GetVotesDown()); + if (voteMax) + { + if (voteMax < 34) + { + float ry = 33.0f/voteMax; + if (voteMax<8) + ry = ry/(8-voteMax); + voteBarHeightUp = (int)(save->GetVotesUp()*ry)-1; + voteBarHeightDown = (int)(save->GetVotesDown()*ry)-1; + } + else + { + float ry = voteMax/33.0f; + voteBarHeightUp = (int)(save->GetVotesUp()/ry)-1; + voteBarHeightDown = (int)(save->GetVotesDown()/ry)-1; + } + } + else + { + voteBarHeightUp = 0; + voteBarHeightDown = 0; + } + } +} + +SaveButton::SaveButton(Point position, Point size, SaveFile * file): + Component(position, size), + save(NULL), + file(file), + thumbnail(NULL), + isMouseInside(false), + isButtonDown(false), + actionCallback(NULL), + selectable(false), + selected(false), + wantsDraw(false), + waitingForThumb(false), + isMouseInsideAuthor(false), + isMouseInsideHistory(false), + showVotes(false) +{ + if(file) + { + name = file->GetDisplayName(); + if(Graphics::textwidth((char *)name.c_str()) > Size.X) + { + int position = Graphics::textwidthx((char *)name.c_str(), Size.X - 22); + name = name.erase(position, name.length()-position); + name += "..."; + } + } +} + +SaveButton::~SaveButton() +{ + RequestBroker::Ref().DetachRequestListener(this); + + if(thumbnail) + delete thumbnail; + if(actionCallback) + delete actionCallback; + if(save) + delete save; + if(file) + delete file; +} + +void SaveButton::OnResponseReady(void * imagePtr) +{ + VideoBuffer * image = (VideoBuffer*)imagePtr; + if(image) + { + if(thumbnail) + delete thumbnail; + thumbnail = image; + waitingForThumb = false; + } +} + +void SaveButton::Tick(float dt) +{ + if(!thumbnail && !waitingForThumb) + { + float scaleFactor = (Size.Y-25)/((float)YRES); + ui::Point thumbBoxSize = ui::Point(((float)XRES)*scaleFactor, ((float)YRES)*scaleFactor); + if(save) + { + if(save->GetGameSave()) + { + waitingForThumb = true; + RequestBroker::Ref().RenderThumbnail(save->GetGameSave(), thumbBoxSize.X, thumbBoxSize.Y, this); + } + else if(save->GetID()) + { + waitingForThumb = true; + RequestBroker::Ref().RetrieveThumbnail(save->GetID(), save->GetVersion(), thumbBoxSize.X, thumbBoxSize.Y, this); + } + } + else if(file && file->GetGameSave()) + { + waitingForThumb = true; + RequestBroker::Ref().RenderThumbnail(file->GetGameSave(), thumbBoxSize.X, thumbBoxSize.Y, this); + } + } +} + +void SaveButton::Draw(const Point& screenPos) +{ + Graphics * g = ui::Engine::Ref().g; + float scaleFactor; + ui::Point thumbBoxSize(0, 0); + + wantsDraw = true; + + if(selected && selectable) + { + g->fillrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 100, 170, 255, 100); + } + + if(thumbnail) + { + thumbBoxSize = ui::Point(thumbnail->Width, thumbnail->Height); + if(save && save->id) + g->draw_image(thumbnail, screenPos.X-3+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, 255); + else + g->draw_image(thumbnail, screenPos.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, 255); + } + else + { + scaleFactor = (Size.Y-25)/((float)YRES); + thumbBoxSize = ui::Point(((float)XRES)*scaleFactor, ((float)YRES)*scaleFactor); + } + if(save) + { + if(save->id) + { + if(isMouseInside) + { + g->drawrect(screenPos.X-3+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 210, 230, 255, 255); + g->drawrect(screenPos.X-4+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, 7, thumbBoxSize.Y, 210, 230, 255, 255); + } + else + { + g->drawrect(screenPos.X-3+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 180, 180, 180, 255); + g->drawrect(screenPos.X-4+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, 7, thumbBoxSize.Y, 180, 180, 180, 255); + } + + g->fillrect(screenPos.X-3+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+1+(Size.Y-20-thumbBoxSize.Y)/2, 5, (thumbBoxSize.Y+1)/2-1, 0, 107, 10, 255); + g->fillrect(screenPos.X-3+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-20)/2, 5, thumbBoxSize.Y/2-1, 107, 10, 0, 255); + + g->fillrect(screenPos.X-2+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-20)/2-voteBarHeightUp, 3, voteBarHeightUp, 57, 187, 57, 255); //green + g->fillrect(screenPos.X-2+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-20)/2, 3, voteBarHeightDown, 187, 57, 57, 255); //red + } + else + { + if(isMouseInside) + g->drawrect(screenPos.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 210, 230, 255, 255); + else + g->drawrect(screenPos.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 180, 180, 180, 255); + } + + if(isMouseInside && !isMouseInsideAuthor) + g->drawtext(screenPos.X+(Size.X-Graphics::textwidth((char *)name.c_str()))/2, screenPos.Y+Size.Y - 21, name, 255, 255, 255, 255); + else + g->drawtext(screenPos.X+(Size.X-Graphics::textwidth((char *)name.c_str()))/2, screenPos.Y+Size.Y - 21, name, 180, 180, 180, 255); + + if(isMouseInsideAuthor) + g->drawtext(screenPos.X+(Size.X-Graphics::textwidth((char *)save->userName.c_str()))/2, screenPos.Y+Size.Y - 10, save->userName, 200, 230, 255, 255); + else + g->drawtext(screenPos.X+(Size.X-Graphics::textwidth((char *)save->userName.c_str()))/2, screenPos.Y+Size.Y - 10, save->userName, 100, 130, 160, 255); + if (showVotes)// && !isMouseInside) + { + int x = screenPos.X-7+(Size.X-thumbBoxSize.X)/2+thumbBoxSize.X-Graphics::textwidth(votesBackground.c_str()); + int y = screenPos.Y-23+(Size.Y-thumbBoxSize.Y)/2+thumbBoxSize.Y; + g->drawtext(x, y, votesBackground, 16, 72, 16, 255); + g->drawtext(x, y, votesBackground2, 192, 192, 192, 255); + g->drawtext(x+3, y, votesString, 255, 255, 255, 255); + } + if (isMouseInsideHistory && showVotes) + { + int x = screenPos.X; + int y = screenPos.Y-15+(Size.Y-thumbBoxSize.Y)/2+thumbBoxSize.Y; + g->fillrect(x+1, y+1, 7, 8, 255, 255, 255, 255); + if (isMouseInsideHistory) { + g->drawtext(x, y, "\xA6", 200, 100, 80, 255); + } else { + g->drawtext(x, y, "\xA6", 160, 70, 50, 255); + } + } + if (!save->GetPublished()) + { + g->drawtext(screenPos.X, screenPos.Y-2, "\xCD", 255, 255, 255, 255); + g->drawtext(screenPos.X, screenPos.Y-2, "\xCE", 212, 151, 81, 255); + } + } + if(file) + { + if(isMouseInside) + g->drawrect(screenPos.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 210, 230, 255, 255); + else + g->drawrect(screenPos.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 180, 180, 180, 255); + + if(isMouseInside) + { + g->drawtext(screenPos.X+(Size.X-Graphics::textwidth((char *)name.c_str()))/2, screenPos.Y+Size.Y - 21, name, 255, 255, 255, 255); + } + else + { + g->drawtext(screenPos.X+(Size.X-Graphics::textwidth((char *)name.c_str()))/2, screenPos.Y+Size.Y - 21, name, 180, 180, 180, 255); + } + } + + if(isMouseInside && selectable) + { + g->clearrect(screenPos.X+(Size.X-20), screenPos.Y+6, 14, 14); + g->drawrect(screenPos.X+(Size.X-20), screenPos.Y+6, 14, 14, 255, 255, 255, 255); + if(selected) + g->fillrect(screenPos.X+(Size.X-18), screenPos.Y+8, 10, 10, 255, 255, 255, 255); + } +} + +void SaveButton::OnMouseUnclick(int x, int y, unsigned int button) +{ + if(button != 1) + { + return; //left click only! + } + + if(x>=Size.X-20 && y>=6 && y<=20 && x<=Size.X-6 && selectable) + { + selected = !selected; + DoSelection(); + return; + } + + if(isButtonDown) + { + isButtonDown = false; + if(isMouseInsideHistory) + DoAltAction(); + else if(isMouseInsideAuthor) + DoAltAction2(); + else + DoAction(); + } +} + +void SaveButton::AddContextMenu(int menuType) +{ + if (menuType == 0) //Save browser + { + menu = new ContextMenu(this); + menu->AddItem(ContextMenuItem("Open", 0, true)); + menu->AddItem(ContextMenuItem("Select", 1, true)); + menu->AddItem(ContextMenuItem("View History", 2, true)); + menu->AddItem(ContextMenuItem("More by this user", 3, true)); + } + else if (menuType == 1) //Local save browser + { + menu = new ContextMenu(this); + menu->AddItem(ContextMenuItem("Open", 0, true)); + menu->AddItem(ContextMenuItem("Rename", 2, true)); + menu->AddItem(ContextMenuItem("Delete", 3, true)); + } +} + +void SaveButton::OnContextMenuAction(int item) +{ + switch(item) + { + case 0: + DoAction(); + break; + case 1: + selected = !selected; + DoSelection(); + break; + case 2: + DoAltAction(); + break; + case 3: + DoAltAction2(); + break; + } +} + +void SaveButton::OnMouseClick(int x, int y, unsigned int button) +{ + if(button == BUTTON_RIGHT) + { + if(menu) + menu->Show(GetScreenPos() + ui::Point(x, y)); + } + else + { + isButtonDown = true; + if(button !=1 && selectable) + { + selected = !selected; + DoSelection(); + } + + } +} + +void SaveButton::OnMouseMovedInside(int x, int y, int dx, int dy) +{ + if(y > Size.Y-11) + isMouseInsideAuthor = true; + else + isMouseInsideAuthor = false; + + if(showVotes && y > Size.Y-29 && y < Size.Y - 18 && x > 0 && x < 9) + isMouseInsideHistory = true; + else + isMouseInsideHistory = false; +} + +void SaveButton::OnMouseEnter(int x, int y) +{ + isMouseInside = true; +} + +void SaveButton::OnMouseLeave(int x, int y) +{ + isMouseInside = false; + isMouseInsideAuthor = false; + isMouseInsideHistory = false; +} + +void SaveButton::DoAltAction() +{ + if(actionCallback) + actionCallback->AltActionCallback(this); +} + +void SaveButton::DoAltAction2() +{ + if(actionCallback) + actionCallback->AltActionCallback2(this); +} + +void SaveButton::DoAction() +{ + if(actionCallback) + actionCallback->ActionCallback(this); +} + +void SaveButton::DoSelection() +{ + if(menu) + { + if(selected) + menu->SetItem(1, "Deselect"); + else + menu->SetItem(1, "Select"); + } + if(selectable && actionCallback) + actionCallback->SelectedCallback(this); +} + +void SaveButton::SetActionCallback(SaveButtonAction * action) +{ + actionCallback = action; +} + +} /* namespace ui */ diff --git a/src/gui/interface/SaveButton.h b/src/gui/interface/SaveButton.h new file mode 100644 index 0000000..7bb77c4 --- /dev/null +++ b/src/gui/interface/SaveButton.h @@ -0,0 +1,83 @@ +#ifndef SAVEBUTTON_H_ +#define SAVEBUTTON_H_ + +#include <string> + +#include "Component.h" +#include "client/SaveFile.h" +#include "client/SaveInfo.h" +#include "client/requestbroker/RequestListener.h" +#include "graphics/Graphics.h" +#include "gui/interface/Colour.h" + +namespace ui +{ +class SaveButton; +class SaveButtonAction +{ +public: + virtual void ActionCallback(ui::SaveButton * sender) {} + virtual void AltActionCallback(ui::SaveButton * sender) {} + virtual void AltActionCallback2(ui::SaveButton * sender) {} + virtual void SelectedCallback(ui::SaveButton * sender) {} + virtual ~SaveButtonAction() {} +}; + +class SaveButton : public Component, public RequestListener +{ + SaveFile * file; + SaveInfo * save; + VideoBuffer * thumbnail; + std::string name; + std::string votesString; + std::string votesBackground; + std::string votesBackground2; + int voteBarHeightUp; + int voteBarHeightDown; + bool wantsDraw; + bool waitingForThumb; + bool isMouseInsideAuthor; + bool isMouseInsideHistory; + bool showVotes; +public: + SaveButton(Point position, Point size, SaveInfo * save); + SaveButton(Point position, Point size, SaveFile * file); + virtual ~SaveButton(); + + virtual void OnMouseClick(int x, int y, unsigned int button); + virtual void OnMouseUnclick(int x, int y, unsigned int button); + + virtual void OnMouseEnter(int x, int y); + virtual void OnMouseLeave(int x, int y); + + virtual void OnMouseMovedInside(int x, int y, int dx, int dy); + + void AddContextMenu(int menuType); + virtual void OnContextMenuAction(int item); + + virtual void Draw(const Point& screenPos); + virtual void Tick(float dt); + + virtual void OnResponseReady(void * imagePtr); + + void SetSelected(bool selected_) { selected = selected_; } + bool GetSelected() { return selected; } + void SetSelectable(bool selectable_) { selectable = selectable_; } + bool GetSelectable() { return selectable; } + void SetShowVotes(bool showVotes_) { showVotes = showVotes_; } + + SaveInfo * GetSave() { return save; } + SaveFile * GetSaveFile() { return file; } + inline bool GetState() { return state; } + virtual void DoAction(); + virtual void DoAltAction(); + virtual void DoAltAction2(); + virtual void DoSelection(); + void SetActionCallback(SaveButtonAction * action); +protected: + bool isButtonDown, state, isMouseInside, selected, selectable; + SaveButtonAction * actionCallback; +}; +} +#endif /* BUTTON_H_ */ + diff --git a/src/gui/interface/ScrollPanel.cpp b/src/gui/interface/ScrollPanel.cpp new file mode 100644 index 0000000..74f6b62 --- /dev/null +++ b/src/gui/interface/ScrollPanel.cpp @@ -0,0 +1,173 @@ +#include <iostream> +#include "ScrollPanel.h" + +using namespace ui; + +ScrollPanel::ScrollPanel(Point position, Point size): + Panel(position, size), + maxOffset(0, 0), + offsetX(0), + offsetY(0), + yScrollVel(0.0f), + xScrollVel(0.0f), + scrollBarWidth(0), + isMouseInsideScrollbar(false), + scrollbarSelected(false), + scrollbarInitialYOffset(0), + scrollbarInitialYClick(0) +{ + +} + +int ScrollPanel::GetScrollLimit() +{ + if(ViewportPosition.Y == 0) + return -1; + else if(maxOffset.Y == -ViewportPosition.Y) + return 1; + return 0; +} + +void ScrollPanel::XOnMouseWheelInside(int localx, int localy, int d) +{ + if(!d) + return; + yScrollVel -= d*2; +} + +void ScrollPanel::Draw(const Point& screenPos) +{ + Panel::Draw(screenPos); + + Graphics * g = ui::Engine::Ref().g; + + //Vertical scroll bar + if(maxOffset.Y>0 && InnerSize.Y>0) + { + float scrollHeight = float(Size.Y)*(float(Size.Y)/float(InnerSize.Y)); + float scrollPos = 0; + if(-ViewportPosition.Y>0) + { + scrollPos = float(Size.Y-scrollHeight)*(float(offsetY)/float(maxOffset.Y)); + } + + g->fillrect(screenPos.X+(Size.X-scrollBarWidth), screenPos.Y, scrollBarWidth, Size.Y, 125, 125, 125, 100); + g->fillrect(screenPos.X+(Size.X-scrollBarWidth), screenPos.Y+scrollPos, scrollBarWidth, scrollHeight, 255, 255, 255, 255); + } +} + +void ScrollPanel::XOnMouseClick(int x, int y, unsigned int button) +{ + if (isMouseInsideScrollbar) + { + scrollbarSelected = true; + scrollbarInitialYOffset = offsetY; + scrollbarInitialYClick = y; + } +} + +void ScrollPanel::XOnMouseUp(int x, int y, unsigned int button) +{ + scrollbarSelected = false; +} + +void ScrollPanel::XOnMouseMoved(int x, int y, int dx, int dy) +{ + if(maxOffset.Y>0 && InnerSize.Y>0) + { + float scrollHeight = float(Size.Y)*(float(Size.Y)/float(InnerSize.Y)); + float scrollPos = 0; + if(-ViewportPosition.Y>0) + { + scrollPos = float(Size.Y-scrollHeight)*(float(offsetY)/float(maxOffset.Y)); + } + + if (scrollbarSelected) + { + if (x > 0) + { + int scrollY = float(y-scrollbarInitialYClick)/float(Size.Y)*float(InnerSize.Y)+scrollbarInitialYOffset; + ViewportPosition.Y = -scrollY; + offsetY = scrollY; + } + else + { + ViewportPosition.Y = -scrollbarInitialYOffset; + offsetY = scrollbarInitialYOffset; + } + } + + if (x > (Size.X-scrollBarWidth) && x < (Size.X-scrollBarWidth)+scrollBarWidth && y > scrollPos && y < scrollPos+scrollHeight) + isMouseInsideScrollbar = true; + else + isMouseInsideScrollbar = false; + } +} + +void ScrollPanel::XTick(float dt) +{ + //if(yScrollVel > 7.0f) yScrollVel = 7.0f; + //if(yScrollVel < -7.0f) yScrollVel = -7.0f; + if(yScrollVel > -0.5f && yScrollVel < 0.5) + yScrollVel = 0; + + if(xScrollVel > 7.0f) xScrollVel = 7.0f; + if(xScrollVel < -7.0f) xScrollVel = -7.0f; + if(xScrollVel > -0.5f && xScrollVel < 0.5) + xScrollVel = 0; + + maxOffset = InnerSize-Size; + maxOffset.Y = std::max(0, maxOffset.Y); + maxOffset.X = std::max(0, maxOffset.X); + + int oldOffsetY = offsetY; + offsetY += yScrollVel; + int oldOffsetX = offsetX; + offsetX += xScrollVel; + + yScrollVel*=0.98f; + xScrollVel*=0.98f; + + if(oldOffsetY!=int(offsetY)) + { + if(offsetY<0) + { + offsetY = 0; + yScrollVel = 0; + //commentsBegin = true; + //commentsEnd = false; + } + else if(offsetY>maxOffset.Y) + { + offsetY = maxOffset.Y; + yScrollVel = 0; + //commentsEnd = true; + //commentsBegin = false; + } + else + { + //commentsEnd = false; + //commentsBegin = false; + } + ViewportPosition.Y = -offsetY; + } + else + { + if(offsetY<0) + { + offsetY = 0; + yScrollVel = 0; + ViewportPosition.Y = -offsetY; + } + else if(offsetY>maxOffset.Y) + { + offsetY = maxOffset.Y; + ViewportPosition.Y = -offsetY; + } + } + + if(mouseInside && scrollBarWidth < 6) + scrollBarWidth++; + else if(!mouseInside && scrollBarWidth > 0 && !scrollbarSelected) + scrollBarWidth--; +}
\ No newline at end of file diff --git a/src/gui/interface/ScrollPanel.h b/src/gui/interface/ScrollPanel.h new file mode 100644 index 0000000..c26c420 --- /dev/null +++ b/src/gui/interface/ScrollPanel.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Panel.h" + +namespace ui +{ + class ScrollPanel: public Panel + { + protected: + int scrollBarWidth; + Point maxOffset; + float offsetX; + float offsetY; + float yScrollVel; + float xScrollVel; + bool isMouseInsideScrollbar; + bool scrollbarSelected; + int scrollbarInitialYOffset; + int scrollbarInitialYClick; + public: + ScrollPanel(Point position, Point size); + + int GetScrollLimit(); + + virtual void Draw(const Point& screenPos); + virtual void XTick(float dt); + virtual void XOnMouseWheelInside(int localx, int localy, int d); + virtual void XOnMouseClick(int localx, int localy, unsigned int button); + virtual void XOnMouseUp(int x, int y, unsigned int button); + virtual void XOnMouseMoved(int localx, int localy, int dx, int dy); + }; +}
\ No newline at end of file diff --git a/src/gui/interface/Slider.cpp b/src/gui/interface/Slider.cpp new file mode 100644 index 0000000..3ada74f --- /dev/null +++ b/src/gui/interface/Slider.cpp @@ -0,0 +1,141 @@ +#include <iostream> +#include "Slider.h" +#include "Colour.h" + +namespace ui { + +Slider::Slider(Point position, Point size, int steps): + Component(position, size), + sliderSteps(steps), + sliderPosition(0), + isMouseDown(false), + bgGradient(NULL), + col1(0, 0, 0, 0), + col2(0, 0, 0, 0) +{ + +} + +void Slider::updatePosition(int position) +{ + if(position < 3) + position = 3; + if(position > Size.X-3) + position = Size.X-3; + + float fPosition = position-3; + float fSize = Size.X-6; + float fSteps = sliderSteps; + + float fSliderPosition = (fPosition/fSize)*sliderSteps;//position;//((x-3)/(Size.X-6))*sliderSteps; + + int newSliderPosition = fSliderPosition; + + if(newSliderPosition == sliderPosition) + return; + + sliderPosition = newSliderPosition; + + if(actionCallback) + { + actionCallback->ValueChangedCallback(this); + } +} + +void Slider::OnMouseMoved(int x, int y, int dx, int dy) +{ + if(isMouseDown) + { + updatePosition(x); + } +} + +void Slider::OnMouseClick(int x, int y, unsigned button) +{ + isMouseDown = true; + updatePosition(x); +} + +void Slider::OnMouseUp(int x, int y, unsigned button) +{ + if(isMouseDown) + { + isMouseDown = false; + } +} + + +void Slider::SetColour(Colour col1, Colour col2) +{ + pixel pix[2] = {PIXRGB(col1.Red, col1.Green, col1.Blue), PIXRGB(col2.Red, col2.Green, col2.Blue)}; + float fl[2] = {0.0f, 1.0f}; + if(bgGradient) + free(bgGradient); + this->col1 = col1; + this->col2 = col2; + bgGradient = (unsigned char*)Graphics::GenerateGradient(pix, fl, 2, Size.X-7); +} + +int Slider::GetValue() +{ + return sliderPosition; +} + +void Slider::SetValue(int value) +{ + if(value < 0) + value = 0; + if(value > sliderSteps) + value = sliderSteps; + sliderPosition = value; +} + +int Slider::GetSteps() +{ + return sliderSteps; +} + +void Slider::SetSteps(int steps) +{ + if(steps < 0) + steps = 0; + if(steps < sliderPosition) + sliderPosition = steps; + sliderSteps = steps; +} + +void Slider::Draw(const Point& screenPos) +{ + Graphics * g = Engine::Ref().g; + //g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 255, 255, 255, 255); + + if(bgGradient) + { +#ifndef OGLI + for (int j = 3; j < Size.Y-7; j++) + for (int i = 3; i < Size.X-7; i++) + g->blendpixel(screenPos.X+i+2, screenPos.Y+j+2, bgGradient[(i-3)*3], bgGradient[(i-3)*3+1], bgGradient[(i-3)*3+2], 255); +#else + g->gradientrect(screenPos.X+5, screenPos.Y+5, Size.X-10, Size.Y-10, col1.Red, col1.Green, col1.Blue, col1.Alpha, col2.Red, col2.Green, col2.Blue, col2.Alpha); +#endif + } + + g->drawrect(screenPos.X+3, screenPos.Y+3, Size.X-6, Size.Y-6, 255, 255, 255, 255); + + float fPosition = sliderPosition; + float fSize = Size.X-6; + float fSteps = sliderSteps; + + float fSliderX = (fSize/fSteps)*fPosition;//sliderPosition;//((Size.X-6)/sliderSteps)*sliderPosition; + int sliderX = fSliderX; + sliderX += 3; + + g->fillrect(screenPos.X+sliderX-2, screenPos.Y+1, 4, Size.Y-2, 20, 20, 20, 255); + g->drawrect(screenPos.X+sliderX-2, screenPos.Y+1, 4, Size.Y-2, 200, 200, 200, 255); +} + +Slider::~Slider() +{ +} + +} /* namespace ui */ diff --git a/src/gui/interface/Slider.h b/src/gui/interface/Slider.h new file mode 100644 index 0000000..b02366e --- /dev/null +++ b/src/gui/interface/Slider.h @@ -0,0 +1,40 @@ +#ifndef SLIDER_H_ +#define SLIDER_H_ + +#include "Component.h" +#include "Colour.h" + +namespace ui { +class Slider; +class SliderAction +{ +public: + virtual void ValueChangedCallback(ui::Slider * sender) {} + virtual ~SliderAction() {} +}; +class Slider: public ui::Component { + friend class SliderAction; + int sliderSteps; + int sliderPosition; + bool isMouseDown; + unsigned char * bgGradient; + SliderAction * actionCallback; + Colour col1, col2; + void updatePosition(int position); +public: + Slider(Point position, Point size, int steps); + virtual void OnMouseMoved(int x, int y, int dx, int dy); + virtual void OnMouseClick(int x, int y, unsigned button); + virtual void OnMouseUp(int x, int y, unsigned button); + virtual void Draw(const Point& screenPos); + void SetColour(Colour col1, Colour col2); + void SetActionCallback(SliderAction * action) { actionCallback = action; } + int GetValue(); + void SetValue(int value); + int GetSteps(); + void SetSteps(int steps); + virtual ~Slider(); +}; + +} /* namespace ui */ +#endif /* SLIDER_H_ */ diff --git a/src/gui/interface/Spinner.cpp b/src/gui/interface/Spinner.cpp new file mode 100644 index 0000000..fd3a61d --- /dev/null +++ b/src/gui/interface/Spinner.cpp @@ -0,0 +1,37 @@ +#include <cmath> +#include <iostream> +#include "Spinner.h" + +using namespace ui; + +Spinner::Spinner(Point position, Point size): + Component(position, size), cValue(0), + tickInternal(0) +{ +} +void Spinner::Tick(float dt) +{ + tickInternal++; + if(tickInternal == 4) + { + cValue += 0.25f;//0.05f; + tickInternal = 0; + } +} +void Spinner::Draw(const Point& screenPos) +{ + Graphics * g = ui::Engine::Ref().g; + int baseX = screenPos.X+(Size.X/2); + int baseY = screenPos.Y+(Size.Y/2); + int lineInner = (Size.X/2); + int lineOuter = (Size.X/2)+3; + for(float t = 0.0f; t < 6.0f; t+=0.25f) + { + //g->drawblob(baseX+(sin(cValue+t)*(Size.X/2)), baseY+(cos(cValue+t)*(Size.X/2)), t*255, t*255, t*255); + g->draw_line(baseX+(sin(cValue+t)*lineInner), baseY+(cos(cValue+t)*lineInner), baseX+(sin(cValue+t)*lineOuter), baseY+(cos(cValue+t)*lineOuter), (t/6)*255, (t/6)*255, (t/6)*255, 255); + } +} +Spinner::~Spinner() +{ + +} diff --git a/src/gui/interface/Spinner.h b/src/gui/interface/Spinner.h new file mode 100644 index 0000000..deabb3c --- /dev/null +++ b/src/gui/interface/Spinner.h @@ -0,0 +1,23 @@ +#ifndef SPINNER_H_ +#define SPINNER_H_ + +#include "Component.h" + +namespace ui +{ + +class Spinner: public Component +{ + float cValue; + int tickInternal; +public: + Spinner(Point position, Point size); + virtual void Tick(float dt); + virtual void Draw(const Point& screenPos); + virtual ~Spinner(); +}; + +} + + +#endif /* SPINNER_H_ */ diff --git a/src/gui/interface/Textbox.cpp b/src/gui/interface/Textbox.cpp new file mode 100644 index 0000000..c23bcb4 --- /dev/null +++ b/src/gui/interface/Textbox.cpp @@ -0,0 +1,707 @@ +#include <string> +#include <iostream> +#include <stdexcept> +#include <time.h> +#include "Config.h" +#include "gui/interface/Point.h" +#include "gui/interface/Textbox.h" +#include "gui/interface/Keys.h" +#include "ContextMenu.h" + +using namespace ui; + +Textbox::Textbox(Point position, Point size, std::string textboxText, std::string textboxPlaceholder): + Label(position, size, ""), + actionCallback(NULL), + masked(false), + border(true), + mouseDown(false), + limit(std::string::npos), + inputType(All), + keyDown(0), + characterDown(0), + ReadOnly(false) +{ + placeHolder = textboxPlaceholder; + + SetText(textboxText); + cursor = text.length(); + + menu->RemoveItem(0); + menu->AddItem(ContextMenuItem("Cut", 1, true)); + menu->AddItem(ContextMenuItem("Copy", 0, true)); + menu->AddItem(ContextMenuItem("Paste", 2, true)); +} + +Textbox::~Textbox() +{ + if(actionCallback) + delete actionCallback; +} + +void Textbox::SetHidden(bool hidden) +{ + menu->RemoveItem(0); + menu->RemoveItem(1); + menu->RemoveItem(2); + menu->AddItem(ContextMenuItem("Cut", 1, !hidden)); + menu->AddItem(ContextMenuItem("Copy", 0, !hidden)); + menu->AddItem(ContextMenuItem("Paste", 2, true)); + + masked = hidden; +} + +void Textbox::SetPlaceholder(std::string text) +{ + placeHolder = text; +} + +void Textbox::SetText(std::string newText) +{ + backingText = newText; + + if(masked) + { + std::string maskedText = std::string(newText); + std::fill(maskedText.begin(), maskedText.end(), '\x8D'); + Label::SetText(maskedText); + } + else + Label::SetText(newText); + + cursor = newText.length(); + + if(cursor) + { + Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY); + } + else + { + cursorPositionY = cursorPositionX = 0; + } +} + +Textbox::ValidInput Textbox::GetInputType() +{ + return inputType; +} + +void Textbox::SetInputType(ValidInput input) +{ + inputType = input; +} + +void Textbox::SetLimit(size_t limit) +{ + this->limit = limit; +} + +size_t Textbox::GetLimit() +{ + return limit; +} + +std::string Textbox::GetText() +{ + return backingText; +} + +void Textbox::OnContextMenuAction(int item) +{ + switch(item) + { + case 0: + copySelection(); + break; + case 1: + cutSelection(); + break; + case 2: + pasteIntoSelection(); + break; + } +} + +void Textbox::cutSelection() +{ + char * clipboardText; + clipboardText = ClipboardPull(); + std::string newText = std::string(clipboardText); + free(clipboardText); + if(HasSelection()) + { + if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length()) + return; + ClipboardPush((char*)backingText.substr(getLowerSelectionBound(), getHigherSelectionBound()-getLowerSelectionBound()).c_str()); + backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound()); + cursor = getLowerSelectionBound(); + } + else + { + ClipboardPush((char*)backingText.c_str()); + backingText.clear(); + } + ClearSelection(); + + if(masked) + { + std::string maskedText = std::string(backingText); + std::fill(maskedText.begin(), maskedText.end(), '\x8D'); + Label::SetText(maskedText); + } + else + { + text = backingText; + } + if(actionCallback) + actionCallback->TextChangedCallback(this); + + if(multiline) + updateMultiline(); + updateSelection(); + TextPosition(text); + + if(cursor) + { + Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY); + } + else + { + cursorPositionY = cursorPositionX = 0; + } +} + +void Textbox::selectAll() +{ + selectionIndex0 = 0; + selectionIndex1 = text.length(); + updateSelection(); +} + +void Textbox::pasteIntoSelection() +{ + char * clipboardText; + clipboardText = ClipboardPull(); + std::string newText = std::string(clipboardText); + free(clipboardText); + if(HasSelection()) + { + if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length()) + return; + backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound()); + cursor = getLowerSelectionBound(); + } + for(std::string::iterator iter = newText.begin(), end = newText.end(); iter != end; ++iter) + { + if(!CharacterValid(*iter)) + { + if(inputType == All) + { + if(*iter == '\n' || *iter == '\r') + *iter = ' '; + else + *iter = '?'; + } + else + *iter = '0'; + } + } + + int regionWidth = Size.X; + if(Appearance.icon) + regionWidth -= 13; + regionWidth -= Appearance.Margin.Left; + regionWidth -= Appearance.Margin.Right; + + if(limit!=std::string::npos) + { + if(limit-backingText.length() >= 0) + newText = newText.substr(0, limit-backingText.length()); + else + newText = ""; + } + else if(!multiline && Graphics::textwidth((char*)std::string(backingText+newText).c_str()) > regionWidth) + { + int pLimit = regionWidth - Graphics::textwidth((char*)backingText.c_str()); + int cIndex = Graphics::CharIndexAtPosition((char *)newText.c_str(), pLimit, 0); + + if(cIndex > 0) + newText = newText.substr(0, cIndex); + else + newText = ""; + } + + backingText.insert(cursor, newText); + cursor = cursor+newText.length(); + ClearSelection(); + + if(masked) + { + std::string maskedText = std::string(backingText); + std::fill(maskedText.begin(), maskedText.end(), '\x8D'); + Label::SetText(maskedText); + } + else + { + text = backingText; + } + if(actionCallback) + actionCallback->TextChangedCallback(this); + + if(multiline) + updateMultiline(); + updateSelection(); + if(multiline) + TextPosition(textLines); + else + TextPosition(text); + + if(cursor) + { + Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY); + } + else + { + cursorPositionY = cursorPositionX = 0; + } +} + +bool Textbox::CharacterValid(Uint16 character) +{ + switch(inputType) + { + case Number: + case Numeric: + return (character >= '0' && character <= '9'); + case All: + default: + return (character >= ' ' && character < 127); + } + return false; +} + +void Textbox::Tick(float dt) +{ + Label::Tick(dt); + if(!IsFocused()) + { + keyDown = 0; + characterDown = 0; + } + if((keyDown || characterDown) && repeatTime <= clock()) + { + OnVKeyPress(keyDown, characterDown, false, false, false); + repeatTime = clock()+(0.03 * CLOCKS_PER_SEC); + } +} + +void Textbox::OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + keyDown = 0; + characterDown = 0; +} + +void Textbox::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + characterDown = character; + keyDown = key; + repeatTime = clock()+(0.3 * CLOCKS_PER_SEC); + OnVKeyPress(key, character, shift, ctrl, alt); +} + +void Textbox::OnVKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + bool changed = false; + if(ctrl && key == 'c' && !masked) + { + copySelection(); + return; + } + if(ctrl && key == 'v' && !ReadOnly) + { + pasteIntoSelection(); + return; + } + if(ctrl && key == 'x' && !masked && !ReadOnly) + { + cutSelection(); + return; + } + if(ctrl && key == 'a') + { + selectAll(); + return; + } + + try + { + switch(key) + { + case KEY_HOME: + cursor = 0; + ClearSelection(); + break; + case KEY_END: + cursor = backingText.length(); + ClearSelection(); + break; + case KEY_LEFT: + if(cursor > 0) + cursor--; + ClearSelection(); + break; + case KEY_RIGHT: + if(cursor < backingText.length()) + cursor++; + ClearSelection(); + break; + case KEY_DELETE: + if(ReadOnly) + break; + if(HasSelection()) + { + if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length()) + return; + backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound()); + cursor = getLowerSelectionBound(); + changed = true; + } + else if(backingText.length() && cursor < backingText.length()) + { + if(ctrl) + backingText.erase(cursor, backingText.length()-cursor); + else + backingText.erase(cursor, 1); + changed = true; + } + ClearSelection(); + break; + case KEY_BACKSPACE: + if(ReadOnly) + break; + if(HasSelection()) + { + if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length()) + return; + backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound()); + cursor = getLowerSelectionBound(); + changed = true; + } + else if(backingText.length() && cursor > 0) + { + if(ctrl) + { + backingText.erase(0, cursor); + cursor = 0; + } + else + { + backingText.erase(cursor-1, 1); + cursor--; + } + changed = true; + } + ClearSelection(); + break; + } + if(CharacterValid(character) && !ReadOnly) + { + if(HasSelection()) + { + if(getLowerSelectionBound() < 0 || getHigherSelectionBound() > backingText.length()) + return; + backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound()); + cursor = getLowerSelectionBound(); + } + + int regionWidth = Size.X; + if(Appearance.icon) + regionWidth -= 13; + regionWidth -= Appearance.Margin.Left; + regionWidth -= Appearance.Margin.Right; + if((limit==std::string::npos || backingText.length() < limit) && (Graphics::textwidth((char*)std::string(backingText+char(character)).c_str()) <= regionWidth || multiline || limit!=std::string::npos)) + { + if(cursor == backingText.length()) + { + backingText += character; + } + else + { + backingText.insert(cursor, 1, (char)character); + } + cursor++; + } + changed = true; + ClearSelection(); + } + } + catch(std::out_of_range &e) + { + cursor = 0; + backingText = ""; + } + if(inputType == Number) + { + //Remove extra preceding 0's + while(backingText[0] == '0' && backingText.length()>1) + backingText.erase(backingText.begin()); + } + if(cursor > backingText.length()) + cursor = backingText.length(); + if(changed) + { + if(masked) + { + std::string maskedText = std::string(backingText); + std::fill(maskedText.begin(), maskedText.end(), '\x8D'); + Label::SetText(maskedText); + } + else + { + text = backingText; + } + if(actionCallback) + actionCallback->TextChangedCallback(this); + } + + if(multiline) + updateMultiline(); + updateSelection(); + if(multiline) + TextPosition(textLines); + else + TextPosition(text); + + if(cursor) + { + Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY); + } + else + { + cursorPositionY = cursorPositionX = 0; + } +} + +void Textbox::OnMouseClick(int x, int y, unsigned button) +{ + + if(button != BUTTON_RIGHT) + { + mouseDown = true; + cursor = Graphics::CharIndexAtPosition(multiline?((char*)textLines.c_str()):((char*)text.c_str()), x-textPosition.X, y-textPosition.Y); + if(cursor) + { + Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY); + } + else + { + cursorPositionY = cursorPositionX = 0; + } + } + Label::OnMouseClick(x, y, button); +} + +void Textbox::OnMouseUp(int x, int y, unsigned button) +{ + mouseDown = false; + Label::OnMouseUp(x, y, button); +} + +void Textbox::OnMouseMoved(int localx, int localy, int dx, int dy) +{ + if(mouseDown) + { + cursor = Graphics::CharIndexAtPosition(multiline?((char*)textLines.c_str()):((char*)text.c_str()), localx-textPosition.X, localy-textPosition.Y); + if(cursor) + { + Graphics::PositionAtCharIndex(multiline?((char*)textLines.c_str()):((char*)text.c_str()), cursor, cursorPositionX, cursorPositionY); + } + else + { + cursorPositionY = cursorPositionX = 0; + } + } + Label::OnMouseMoved(localx, localy, dx, dy); +} + +void Textbox::Draw(const Point& screenPos) +{ + Label::Draw(screenPos); + + Graphics * g = Engine::Ref().g; + if(IsFocused()) + { + if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 255, 255, 255, 255); + g->draw_line(screenPos.X+textPosition.X+cursorPositionX, screenPos.Y-2+textPosition.Y+cursorPositionY, screenPos.X+textPosition.X+cursorPositionX, screenPos.Y+9+textPosition.Y+cursorPositionY, 255, 255, 255, 255); + } + else + { + if(!text.length()) + { + g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, placeHolder, textColour.Red, textColour.Green, textColour.Blue, 170); + } + if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 160, 160, 160, 255); + } + if(Appearance.icon) + g->draw_icon(screenPos.X+iconPosition.X, screenPos.Y+iconPosition.Y, Appearance.icon); +} + +/* +Textbox::Textbox(Point position, Point size, std::string textboxText): + Component(position, size), + text(textboxText), + actionCallback(NULL), + masked(false), + border(true) +{ + SetText(textboxText); + cursor = text.length(); +} + +Textbox::~Textbox() +{ + if(actionCallback) + delete actionCallback; +} + +void Textbox::TextPosition() +{ + if(cursor) + { + cursorPosition = Graphics::textnwidth((char *)displayText.c_str(), cursor); + } + else + { + cursorPosition = 0; + } + Component::TextPosition(displayText); +} + +void Textbox::SetText(std::string text) +{ + cursor = text.length(); + this->text = text; + this->displayText = text; + TextPosition(); +} + + +void Textbox::SetDisplayText(std::string text) +{ + displayText = text; + TextPosition(); +} + +std::string Textbox::GetText() +{ + return text; +} + +void Textbox::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + bool changed = false; + try + { + switch(key) + { + case KEY_HOME: + cursor = 0; + break; + case KEY_END: + cursor = text.length(); + break; + case KEY_LEFT: + if(cursor > 0) + cursor--; + break; + case KEY_RIGHT: + if(cursor < text.length()) + cursor++; + break; + case KEY_DELETE: + if(text.length() && cursor < text.length()) + { + if(ctrl) + text.erase(cursor, text.length()-cursor); + else + text.erase(cursor, 1); + changed = true; + } + break; + case KEY_BACKSPACE: + if(text.length() && cursor > 0) + { + if(ctrl) + { + text.erase(0, cursor); + cursor = 0; + } + else + { + text.erase(cursor-1, 1); + cursor--; + } + changed = true; + } + break; + } + if(character >= ' ' && character < 127) + { + if(cursor == text.length()) + { + text += character; + } + else + { + text.insert(cursor, 1, (char)character); + } + cursor++; + changed = true; + } + } + catch(std::out_of_range &e) + { + cursor = 0; + text = ""; + } + if(changed) + { + if(masked) + { + char * tempText = new char[text.length()+1]; + std::fill(tempText, tempText+text.length(), 0x8d); + tempText[text.length()] = 0; + displayText = std::string(tempText); + delete tempText; + } + else + { + displayText = text; + } + if(actionCallback) + actionCallback->TextChangedCallback(this); + } + TextPosition(); +} + +void Textbox::Draw(const Point& screenPos) +{ + if(!drawn) + { + TextPosition(); + drawn = true; + } + Graphics * g = Engine::Ref().g; + if(IsFocused()) + { + if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 255, 255, 255, 255); + g->draw_line(screenPos.X+textPosition.X+cursorPosition, screenPos.Y+3, screenPos.X+textPosition.X+cursorPosition, screenPos.Y+12, 255, 255, 255, XRES+BARSIZE); + } + else + { + if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 160, 160, 160, 255); + } + g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, displayText, 255, 255, 255, 255); + if(Appearance.icon) + g->draw_icon(screenPos.X+iconPosition.X, screenPos.Y+iconPosition.Y, Appearance.icon); +}*/ diff --git a/src/gui/interface/Textbox.h b/src/gui/interface/Textbox.h new file mode 100644 index 0000000..0d5140b --- /dev/null +++ b/src/gui/interface/Textbox.h @@ -0,0 +1,108 @@ +#ifndef TEXTBOX_H +#define TEXTBOX_H + +#include <string> + +#include "Label.h" +#include "PowderToy.h" + +namespace ui +{ +class Textbox; +class TextboxAction +{ +public: + virtual void TextChangedCallback(ui::Textbox * sender) {} + virtual ~TextboxAction() {} +}; + +class Textbox : public Label +{ + friend class TextboxAction; +public: + bool ReadOnly; + enum ValidInput { All, Numeric, Number }; + Textbox(Point position, Point size, std::string textboxText = "", std::string textboxPlaceholder = ""); + virtual ~Textbox(); + + virtual void SetText(std::string text); + virtual std::string GetText(); + + virtual void SetPlaceholder(std::string text); + + void SetBorder(bool border) { this->border = border; }; + void SetHidden(bool hidden); + bool GetHidden() { return masked; } + void SetActionCallback(TextboxAction * action) { actionCallback = action; } + + void SetLimit(size_t limit); + size_t GetLimit(); + + ValidInput GetInputType(); + void SetInputType(ValidInput input); + + //Determines if the given character is valid given the input type + bool CharacterValid(Uint16 character); + + virtual void Tick(float dt); + virtual void OnContextMenuAction(int item); + virtual void OnMouseClick(int x, int y, unsigned button); + virtual void OnMouseUp(int x, int y, unsigned button); + virtual void OnMouseMoved(int localx, int localy, int dx, int dy); + virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + virtual void OnVKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + virtual void OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt); + virtual void Draw(const Point& screenPos); + +protected: + ValidInput inputType; + size_t limit; + int repeatTime; + int keyDown; + Uint16 characterDown; + bool mouseDown; + bool masked, border; + int cursor, cursorPositionX, cursorPositionY; + TextboxAction *actionCallback; + std::string backingText; + std::string placeHolder; + + virtual void selectAll(); + virtual void cutSelection(); + virtual void pasteIntoSelection(); +}; + +/*class Textbox : public Component +{ + friend class TextboxAction; +protected: + std::string text; + std::string displayText; + int cursor, cursorPosition; + TextboxAction *actionCallback; + bool masked; + bool border; +public: + Textbox(Point position, Point size, std::string textboxText); + virtual ~Textbox(); + + virtual void SetText(std::string text); + virtual void SetDisplayText(std::string text); + std::string GetText(); + void SetActionCallback(TextboxAction * action) { actionCallback = action; } + virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + + void SetHidden(bool hidden) { masked = hidden; } + bool GetHidden() { return masked; } + + void SetBorder(bool border) {this->border = border;} + + void TextPosition(); + + virtual void Draw(const Point& screenPos); +}; +}*/ +} + + +#endif // TEXTBOX_H diff --git a/src/gui/interface/Window.cpp b/src/gui/interface/Window.cpp new file mode 100644 index 0000000..9ec6857 --- /dev/null +++ b/src/gui/interface/Window.cpp @@ -0,0 +1,558 @@ +#include <iostream> +#include "Window.h" +#include "Keys.h" +#include "Component.h" +#include "gui/interface/Point.h" +#include "gui/interface/Button.h" + +using namespace ui; + +Window::Window(Point _position, Point _size): + Position(_position), + Size(_size), + focusedComponent_(NULL), + AllowExclusiveDrawing(true), + halt(false), + destruct(false), + stop(false), + cancelButton(NULL), + okayButton(NULL) +#ifdef DEBUG + ,debugMode(false) +#endif +{ +} + +Window::~Window() +{ + for(unsigned i = 0, sz = Components.size(); i < sz; ++i) + if( Components[i] ) + { + delete Components[i]; + if(Components[i]==focusedComponent_) + focusedComponent_ = NULL; + } + Components.clear(); +} + +void Window::AddComponent(Component* c) +{ + // TODO: do a check if component was already added? + if(c->GetParentWindow()==NULL) + { + c->SetParentWindow(this); + Components.push_back(c); + + if(Engine::Ref().GetMouseX() > Position.X + c->Position.X && Engine::Ref().GetMouseX() < Position.X + c->Position.X + c->Size.X && + Engine::Ref().GetMouseY() > Position.Y + c->Position.Y && Engine::Ref().GetMouseY() < Position.Y + c->Position.Y + c->Size.Y) + c->OnMouseEnter(Engine::Ref().GetMouseX() - (Position.X + c->Position.X), Engine::Ref().GetMouseY() - (Position.Y + c->Position.Y)); + } + else + { + //Component already has a state, don't sad it + } +} + +unsigned Window::GetComponentCount() +{ + return Components.size(); +} + +Component* Window::GetComponent(unsigned idx) +{ + return Components[idx]; +} + +void Window::RemoveComponent(Component* c) +{ + // remove component WITHOUT freeing it. + for(unsigned i = 0; i < Components.size(); ++i) + { + // find the appropriate component index + if(Components[i] == c) + { + //Make sure any events don't continue + halt = true; + if(Components[i]==focusedComponent_) + focusedComponent_ = NULL; + + Components.erase(Components.begin() + i); + + // we're done + return; + } + } +} + +void Window::OnTryExit(ExitMethod method) +{ + if(cancelButton && method != MouseOutside) + cancelButton->DoAction(); +} + +void Window::OnTryOkay(OkayMethod method) +{ + if(okayButton) + okayButton->DoAction(); +} + +void Window::RemoveComponent(unsigned idx) +{ + halt = true; + // free component and remove it. + if(Components[idx]==focusedComponent_) + focusedComponent_ = NULL; + delete Components[idx]; + Components.erase(Components.begin() + idx); +} + +bool Window::IsFocused(const Component* c) const +{ + return c == focusedComponent_; +} + +void Window::FocusComponent(Component* c) +{ + this->focusedComponent_ = c; +} + +void Window::DoExit() +{ + + OnExit(); +} + +void Window::DoInitialized() +{ + + OnInitialized(); +} + +void Window::DoBlur() +{ + + OnBlur(); +} + +void Window::DoFocus() +{ + + OnFocus(); +} + +void Window::DoDraw() +{ + OnDraw(); + //draw + for(int i = 0, sz = Components.size(); i < sz; ++i) + if(Components[i]->Visible) + { + if(AllowExclusiveDrawing) + { + Point scrpos(Components[i]->Position.X + Position.X, Components[i]->Position.Y + Position.Y); + Components[i]->Draw(scrpos); + } + else + { + if( Components[i]->Position.X+Position.X + Components[i]->Size.X >= 0 && + Components[i]->Position.Y+Position.Y + Components[i]->Size.Y >= 0 && + Components[i]->Position.X+Position.X < ui::Engine::Ref().GetWidth() && + Components[i]->Position.Y+Position.Y < ui::Engine::Ref().GetHeight() ) + { + Point scrpos(Components[i]->Position.X + Position.X, Components[i]->Position.Y + Position.Y); + Components[i]->Draw( Point(scrpos) ); + } + } +#ifdef DEBUG + if(debugMode) + { + if(focusedComponent_==Components[i]) + { + ui::Engine::Ref().g->fillrect(Components[i]->Position.X+Position.X, Components[i]->Position.Y+Position.Y, Components[i]->Size.X, Components[i]->Size.Y, 0, 255, 0, 90); + } + else + { + ui::Engine::Ref().g->fillrect(Components[i]->Position.X+Position.X, Components[i]->Position.Y+Position.Y, Components[i]->Size.X, Components[i]->Size.Y, 255, 0, 0, 90); + } + } +#endif + } +#ifdef DEBUG + if(debugMode) + { + if(focusedComponent_) + { + int xPos = focusedComponent_->Position.X+focusedComponent_->Size.X+5+Position.X; + Graphics * g = ui::Engine::Ref().g; + char tempString[512]; + char tempString2[512]; + + sprintf(tempString, "Position: L %d, R %d, T: %d, B: %d", focusedComponent_->Position.X, Size.X-(focusedComponent_->Position.X+focusedComponent_->Size.X), focusedComponent_->Position.Y, Size.Y-(focusedComponent_->Position.Y+focusedComponent_->Size.Y)); + sprintf(tempString2, "Size: %d, %d", focusedComponent_->Size.X, focusedComponent_->Size.Y); + + if(Graphics::textwidth(tempString)+xPos > XRES+BARSIZE) + xPos = XRES+BARSIZE-(Graphics::textwidth(tempString)+5); + if(Graphics::textwidth(tempString2)+xPos > XRES+BARSIZE) + xPos = XRES+BARSIZE-(Graphics::textwidth(tempString2)+5); + + g->drawtext(xPos, focusedComponent_->Position.Y+Position.Y+1, tempString, 0, 0, 0, 200); + g->drawtext(xPos, focusedComponent_->Position.Y+Position.Y, tempString, 255, 255, 255, 255); + g->drawtext(xPos, focusedComponent_->Position.Y+Position.Y+13, tempString2, 0, 0, 0, 200); + g->drawtext(xPos, focusedComponent_->Position.Y+Position.Y+12, tempString2, 255, 255, 255, 255); + } + return; + } +#endif + +} + +void Window::DoTick(float dt) +{ +#ifdef DEBUG + if(debugMode) + return; +#endif + //on mouse hover + for(int i = Components.size() - 1; i >= 0 && !halt; --i) + { + if(!Components[i]->Locked && + ui::Engine::Ref().GetMouseX() >= Components[i]->Position.X+Position.X && + ui::Engine::Ref().GetMouseY() >= Components[i]->Position.Y+Position.Y && + ui::Engine::Ref().GetMouseX() < Components[i]->Position.X+Position.X + Components[i]->Size.X && + ui::Engine::Ref().GetMouseY() < Components[i]->Position.Y+Position.Y + Components[i]->Size.Y ) + { + Components[i]->OnMouseHover(ui::Engine::Ref().GetMouseX() - (Components[i]->Position.X + Position.X), ui::Engine::Ref().GetMouseY() - (Components[i]->Position.Y + Position.Y)); + break; + } + } + + //tick + for(int i = 0, sz = Components.size(); i < sz && !halt; ++i) + { + Components[i]->Tick(dt); + } + + halt = false; + stop = false; + + OnTick(dt); + + if(destruct) + finalise(); +} + +void Window::DoKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ +#ifdef DEBUG + if(key == KEY_TAB && ctrl) + debugMode = !debugMode; + if(debugMode) + { + if(focusedComponent_!=NULL) + { + if(shift) + { + if(key == KEY_UP) + focusedComponent_->Size.Y--; + if(key == KEY_DOWN) + focusedComponent_->Size.Y++; + if(key == KEY_LEFT) + focusedComponent_->Size.X--; + if(key == KEY_RIGHT) + focusedComponent_->Size.X++; + } + if(ctrl) + { + if(key == KEY_UP) + focusedComponent_->Size.Y++; + if(key == KEY_DOWN) + focusedComponent_->Size.Y--; + if(key == KEY_LEFT) + focusedComponent_->Size.X++; + if(key == KEY_RIGHT) + focusedComponent_->Size.X--; + } + if(!shift) + { + if(key == KEY_UP) + focusedComponent_->Position.Y--; + if(key == KEY_DOWN) + focusedComponent_->Position.Y++; + if(key == KEY_LEFT) + focusedComponent_->Position.X--; + if(key == KEY_RIGHT) + focusedComponent_->Position.X++; + } + if (key == KEY_DELETE) + { + RemoveComponent(focusedComponent_); + halt = false; + } + } + else + { + if(shift) + { + if(key == KEY_UP) + Size.Y--; + if(key == KEY_DOWN) + Size.Y++; + if(key == KEY_LEFT) + Size.X--; + if(key == KEY_RIGHT) + Size.X++; + } + if(ctrl) + { + if(key == KEY_UP) + Size.Y++; + if(key == KEY_DOWN) + Size.Y--; + if(key == KEY_LEFT) + Size.X++; + if(key == KEY_RIGHT) + Size.X--; + } + if(!shift) + { + if(key == KEY_UP) + Position.Y--; + if(key == KEY_DOWN) + Position.Y++; + if(key == KEY_LEFT) + Position.X--; + if(key == KEY_RIGHT) + Position.X++; + } + } + return; + } +#endif + //on key press + if(focusedComponent_ != NULL) + { + if(!focusedComponent_->Locked && focusedComponent_->Visible) + focusedComponent_->OnKeyPress(key, character, shift, ctrl, alt); + } + + if(!stop) + OnKeyPress(key, character, shift, ctrl, alt); + + if(key == KEY_ESCAPE) + OnTryExit(Escape); + + if(key == KEY_ENTER || key == KEY_RETURN) + OnTryOkay(Enter); + + if(destruct) + finalise(); +} + +void Window::DoKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ +#ifdef DEBUG + if(debugMode) + return; +#endif + //on key unpress + if(focusedComponent_ != NULL) + { + if(!focusedComponent_->Locked && focusedComponent_->Visible) + focusedComponent_->OnKeyRelease(key, character, shift, ctrl, alt); + } + + if(!stop) + OnKeyRelease(key, character, shift, ctrl, alt); + if(destruct) + finalise(); +} + +void Window::DoMouseDown(int x_, int y_, unsigned button) +{ + //on mouse click + int x = x_ - Position.X; + int y = y_ - Position.Y; + bool clickState = false; + for(int i = Components.size() - 1; i > -1 && !halt; --i) + { + if(!Components[i]->Locked && Components[i]->Visible) + { + if(x >= Components[i]->Position.X && y >= Components[i]->Position.Y && x < Components[i]->Position.X + Components[i]->Size.X && y < Components[i]->Position.Y + Components[i]->Size.Y) + { + FocusComponent(Components[i]); +#ifdef DEBUG + if(!debugMode) +#endif + Components[i]->OnMouseClick(x - Components[i]->Position.X, y - Components[i]->Position.Y, button); + clickState = true; + break; + } + } + } + + if(!clickState) + FocusComponent(NULL); + +#ifdef DEBUG + if(debugMode) + return; +#endif + + //on mouse down + for(int i = Components.size() - 1; i > -1 && !halt; --i) + { + if(!Components[i]->Locked && Components[i]->Visible) + Components[i]->OnMouseDown(x, y, button); + } + + if(!stop) + OnMouseDown(x_, y_, button); + + if(!clickState && (x_ < Position.X || y_ < Position.Y || x_ > Position.X+Size.X || y_ > Position.Y+Size.Y)) + OnTryExit(MouseOutside); + + if(destruct) + finalise(); +} + +void Window::DoMouseMove(int x_, int y_, int dx, int dy) +{ + //on mouse move (if true, and inside) + int x = x_ - Position.X; + int y = y_ - Position.Y; +#ifdef DEBUG + if(debugMode) + return; +#endif + for(int i = Components.size() - 1; i > -1 && !halt; --i) + { + if(!Components[i]->Locked && Components[i]->Visible) + { + Point local (x - Components[i]->Position.X, y - Components[i]->Position.Y) + , a (local.X - dx, local.Y - dy); + + Components[i]->OnMouseMoved(local.X, local.Y, dx, dy); + + if(local.X >= 0 && + local.Y >= 0 && + local.X < Components[i]->Size.X && + local.Y < Components[i]->Size.Y && !halt) + { + Components[i]->OnMouseMovedInside(local.X, local.Y, dx, dy); + + // entering? + if(!( + a.X >= 0 && + a.Y >= 0 && + a.X < Components[i]->Size.X && + a.Y < Components[i]->Size.Y )) + { + Components[i]->OnMouseEnter(local.X, local.Y); + } + } + else if(!halt) + { + // leaving? + if( a.X >= 0 && + a.Y >= 0 && + a.X < Components[i]->Size.X && + a.Y < Components[i]->Size.Y ) + { + Components[i]->OnMouseLeave(local.X, local.Y); + } + + } + } + } + + if(!stop) + OnMouseMove(x_, y_, dx, dy); + if(destruct) + finalise(); +} + +void Window::DoMouseUp(int x_, int y_, unsigned button) +{ + int x = x_ - Position.X; + int y = y_ - Position.Y; +#ifdef DEBUG + if(debugMode) + return; +#endif + //on mouse unclick + for(int i = Components.size() - 1; i >= 0 && !halt; --i) + { + if(!Components[i]->Locked && Components[i]->Visible) + { + if(x >= Components[i]->Position.X && y >= Components[i]->Position.Y && x < Components[i]->Position.X + Components[i]->Size.X && y < Components[i]->Position.Y + Components[i]->Size.Y) + { + Components[i]->OnMouseUnclick(x - Components[i]->Position.X, y - Components[i]->Position.Y, button); + break; + } + } + } + + //on mouse up + for(int i = Components.size() - 1; i >= 0 && !halt; --i) + { + if(!Components[i]->Locked && Components[i]->Visible) + Components[i]->OnMouseUp(x, y, button); + } + + if(!stop) + OnMouseUp(x_, y_, button); + if(destruct) + finalise(); +} + +void Window::DoMouseWheel(int x_, int y_, int d) +{ + int x = x_ - Position.X; + int y = y_ - Position.Y; +#ifdef DEBUG + if(debugMode) + return; +#endif + //on mouse wheel focused + for(int i = Components.size() - 1; i >= 0 && !halt; --i) + { + if(x >= Components[i]->Position.X && y >= Components[i]->Position.Y && x < Components[i]->Position.X + Components[i]->Size.X && y < Components[i]->Position.Y + Components[i]->Size.Y) + { + if(!Components[i]->Locked && Components[i]->Visible) + Components[i]->OnMouseWheelInside(x - Components[i]->Position.X, y - Components[i]->Position.Y, d); + break; + } + } + + //on mouse wheel + for(int i = Components.size() - 1; i >= 0 && !halt; --i) + { + if(!Components[i]->Locked && Components[i]->Visible) + Components[i]->OnMouseWheel(x - Components[i]->Position.X, y - Components[i]->Position.Y, d); + } + + if(!stop) + OnMouseWheel(x_, y_, d); + + if(destruct) + finalise(); +} + +void Window::finalise() +{ + delete this; +} + +void Window::SelfDestruct() +{ + destruct = true; + halt = true; + stop = true; +} + +void Window::Halt() +{ + stop = true; + halt = true; +} + diff --git a/src/gui/interface/Window.h b/src/gui/interface/Window.h new file mode 100644 index 0000000..dafb910 --- /dev/null +++ b/src/gui/interface/Window.h @@ -0,0 +1,135 @@ +#ifndef WINDOW_H +#define WINDOW_H + +#include <vector> +#include "gui/interface/Point.h" +#include "Engine.h" + +namespace ui +{ + +enum ChromeStyle +{ + None, Title, Resizable +}; +//class State; + class Engine; + class Component; + class Button; + + /* class State + * + * A UI state. Contains all components. + */ + class Window + { + public: + Point Position; + Point Size; + + Window(Point _position, Point _size); + virtual ~Window(); + + void SetOkayButton(ui::Button * button) { okayButton = button; } + void SetCancelButton(ui::Button * button) { cancelButton = button; } + + bool AllowExclusiveDrawing; //false will not call draw on objects outside of bounds + + // Add Component to state + void AddComponent(Component* c); + + // Get the number of components this state has. + unsigned GetComponentCount(); + + // Get component by index. (See GetComponentCount()) + Component* GetComponent(unsigned idx); + + // Remove a component from state. NOTE: This DOES NOT free component from memory. + void RemoveComponent(Component* c); + + // Remove a component from state. NOTE: This WILL free component from memory. + void RemoveComponent(unsigned idx); + + virtual void ToolTip(Component * sender, ui::Point mousePosition, std::string toolTip) {} + + virtual void DoInitialized(); + virtual void DoExit(); + virtual void DoTick(float dt); + virtual void DoDraw(); + virtual void DoFocus(); + virtual void DoBlur(); + + virtual void DoMouseMove(int x, int y, int dx, int dy); + virtual void DoMouseDown(int x, int y, unsigned button); + virtual void DoMouseUp(int x, int y, unsigned button); + virtual void DoMouseWheel(int x, int y, int d); + virtual void DoKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + virtual void DoKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt); + + //Sets halt and destroy, this causes the Windows to stop sending events and remove itself. + void SelfDestruct(); + void Halt(); + + bool IsFocused(const Component* c) const; + void FocusComponent(Component* c); + + void* UserData; + + enum OkayMethod { Enter, OkayButton }; + enum ExitMethod { MouseOutside, Escape, ExitButton }; + + protected: + ui::Button * okayButton; + ui::Button * cancelButton; + + virtual void OnInitialized() {} + virtual void OnExit() {} + virtual void OnTick(float dt) {} + virtual void OnDraw() {} + virtual void OnFocus() {} + virtual void OnBlur() {} + + virtual void OnTryExit(ExitMethod); + virtual void OnTryOkay(OkayMethod); + + virtual void OnMouseMove(int x, int y, int dx, int dy) {} + virtual void OnMouseDown(int x, int y, unsigned button) {} + virtual void OnMouseUp(int x, int y, unsigned button) {} + virtual void OnMouseWheel(int x, int y, int d) {} + virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) {} + virtual void OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt) {} + std::vector<Component*> Components; + Component* focusedComponent_; + ChromeStyle chrome; + + //These controls allow a component to call the destruction of the Window inside an event (called by the Window) + void finalise(); + bool halt; + bool destruct; + bool stop; +#ifdef DEBUG + bool debugMode; +#endif + + }; + + +/*class Window : public State +{ +private: + ChromeStyle chrome; +public: + Window(Point _position, Point _size); + Point Position; + Point Size; + + virtual void DoTick(float dt); + virtual void DoDraw(); + + virtual void DoMouseMove(int x, int y, int dx, int dy); + virtual void DoMouseDown(int x, int y, unsigned button); + virtual void DoMouseUp(int x, int y, unsigned button); + virtual void DoMouseWheel(int x, int y, int d); +};*/ +} +#endif // WINDOW_H diff --git a/src/gui/localbrowser/LocalBrowserController.cpp b/src/gui/localbrowser/LocalBrowserController.cpp new file mode 100644 index 0000000..a1a01ed --- /dev/null +++ b/src/gui/localbrowser/LocalBrowserController.cpp @@ -0,0 +1,176 @@ +#include <sstream> + +#include "client/Client.h" +#include "LocalBrowserController.h" +#include "gui/interface/Engine.h" +#include "gui/dialogues/ConfirmPrompt.h" +#include "tasks/TaskWindow.h" +#include "tasks/Task.h" + +#include "LocalBrowserModel.h" +#include "LocalBrowserView.h" + +LocalBrowserController::LocalBrowserController(ControllerCallback * callback): + HasDone(false) +{ + browserModel = new LocalBrowserModel(); + browserView = new LocalBrowserView(); + browserView->AttachController(this); + browserModel->AddObserver(browserView); + + this->callback = callback; + + browserModel->UpdateSavesList(1); +} + +void LocalBrowserController::OpenSave(SaveFile * save) +{ + browserModel->SetSave(save); +} + +SaveFile * LocalBrowserController::GetSave() +{ + return browserModel->GetSave(); +} + +void LocalBrowserController::RemoveSelected() +{ + class RemoveSelectedConfirmation: public ConfirmDialogueCallback { + public: + LocalBrowserController * c; + RemoveSelectedConfirmation(LocalBrowserController * c_) { c = c_; } + virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) { + if (result == ConfirmPrompt::ResultOkay) + c->removeSelectedC(); + } + virtual ~RemoveSelectedConfirmation() { } + }; + + std::stringstream desc; + desc << "Are you sure you want to delete " << browserModel->GetSelected().size() << " stamp"; + if(browserModel->GetSelected().size()>1) + desc << "s"; + new ConfirmPrompt("Delete stamps", desc.str(), new RemoveSelectedConfirmation(this)); +} + +void LocalBrowserController::removeSelectedC() +{ + class RemoveSavesTask : public Task + { + std::vector<std::string> saves; + LocalBrowserController * c; + public: + RemoveSavesTask(LocalBrowserController * c, std::vector<std::string> saves_) : c(c) { saves = saves_; } + virtual bool doWork() + { + for(int i = 0; i < saves.size(); i++) + { + std::stringstream saveName; + saveName << "Deleting stamp [" << saves[i] << "] ..."; + notifyStatus(saveName.str()); + Client::Ref().DeleteStamp(saves[i]); + notifyProgress((float(i+1)/float(saves.size())*100)); + } + return true; + } + virtual void after() + { + c->RefreshSavesList(); + } + }; + + std::vector<std::string> selected = browserModel->GetSelected(); + new TaskWindow("Removing stamps", new RemoveSavesTask(this, selected)); +} + +void LocalBrowserController::RescanStamps() +{ + class RescanConfirmation: public ConfirmDialogueCallback { + public: + LocalBrowserController * c; + RescanConfirmation(LocalBrowserController * c_) { c = c_; } + virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) { + if (result == ConfirmPrompt::ResultOkay) + c->rescanStampsC(); + } + virtual ~RescanConfirmation() { } + }; + + std::stringstream desc; + desc << "Rescanning the stamps folder can find stamps added to the stamps folder or recover stamps when the stamps.def file has been lost or damaged. However, be warned that this will mess up the current sorting order"; + new ConfirmPrompt("Rescan", desc.str(), new RescanConfirmation(this)); +} + +void LocalBrowserController::rescanStampsC() +{ + browserModel->RescanStamps(); + browserModel->UpdateSavesList(browserModel->GetPageNum()); +} + +void LocalBrowserController::RefreshSavesList() +{ + ClearSelection(); + browserModel->UpdateSavesList(browserModel->GetPageNum()); +} + +void LocalBrowserController::ClearSelection() +{ + browserModel->ClearSelected(); +} + +void LocalBrowserController::NextPage() +{ + if(browserModel->GetPageNum() < browserModel->GetPageCount()) + browserModel->UpdateSavesList(browserModel->GetPageNum()+1); +} + +void LocalBrowserController::PrevPage() +{ + if(browserModel->GetPageNum()>1) + browserModel->UpdateSavesList(browserModel->GetPageNum()-1); +} + +void LocalBrowserController::Update() +{ + if(browserModel->GetSave()) + { + Exit(); + } +} + +void LocalBrowserController::Selected(std::string saveName, bool selected) +{ + if(selected) + browserModel->SelectSave(saveName); + else + browserModel->DeselectSave(saveName); +} + +bool LocalBrowserController::GetMoveToFront() +{ + return browserModel->GetMoveToFront(); +} + +void LocalBrowserController::SetMoveToFront(bool move) +{ + browserModel->SetMoveToFront(move); +} + +void LocalBrowserController::Exit() +{ + if(ui::Engine::Ref().GetWindow() == browserView) + ui::Engine::Ref().CloseWindow(); + if(callback) + callback->ControllerExit(); + HasDone = true; +} + +LocalBrowserController::~LocalBrowserController() { + if(ui::Engine::Ref().GetWindow() == browserView) + ui::Engine::Ref().CloseWindow(); + if(callback) + delete callback; + delete browserModel; + delete browserView; +} + diff --git a/src/gui/localbrowser/LocalBrowserController.h b/src/gui/localbrowser/LocalBrowserController.h new file mode 100644 index 0000000..6dcf2a5 --- /dev/null +++ b/src/gui/localbrowser/LocalBrowserController.h @@ -0,0 +1,36 @@ +#ifndef STAMPSCONTROLLER_H_ +#define STAMPSCONTROLLER_H_ + +#include "Controller.h" +#include "LocalBrowserView.h" +#include "client/SaveInfo.h" + +class LocalBrowserView; +class LocalBrowserModel; +class LocalBrowserController { + ControllerCallback * callback; + LocalBrowserView * browserView; + LocalBrowserModel * browserModel; +public: + bool HasDone; + LocalBrowserController(ControllerCallback * callback); + LocalBrowserView * GetView() {return browserView;} + SaveFile * GetSave(); + void RemoveSelected(); + void removeSelectedC(); + void ClearSelection(); + void Selected(std::string stampID, bool selected); + void RescanStamps(); + void rescanStampsC(); + void RefreshSavesList(); + void OpenSave(SaveFile * stamp); + bool GetMoveToFront(); + void SetMoveToFront(bool move); + void NextPage(); + void PrevPage(); + void Update(); + void Exit(); + virtual ~LocalBrowserController(); +}; + +#endif /* STAMPSCONTROLLER_H_ */ diff --git a/src/gui/localbrowser/LocalBrowserModel.cpp b/src/gui/localbrowser/LocalBrowserModel.cpp new file mode 100644 index 0000000..333e845 --- /dev/null +++ b/src/gui/localbrowser/LocalBrowserModel.cpp @@ -0,0 +1,146 @@ +#include "LocalBrowserModel.h" +#include "LocalBrowserView.h" +#include "LocalBrowserModelException.h" +#include "client/Client.h" +#include "client/SaveFile.h" + +LocalBrowserModel::LocalBrowserModel(): + stamp(NULL), + currentPage(1), + stampToFront(1) +{ + //stampIDs = Client::Ref().GetStamps(); + stampIDs = Client::Ref().GetStamps(0, 16); +} + + +std::vector<SaveFile*> LocalBrowserModel::GetSavesList() +{ + return savesList; +} + +void LocalBrowserModel::AddObserver(LocalBrowserView * observer) +{ + observers.push_back(observer); + observer->NotifySavesListChanged(this); + observer->NotifyPageChanged(this); +} + +void LocalBrowserModel::notifySavesListChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifySavesListChanged(this); + } +} + +void LocalBrowserModel::notifyPageChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyPageChanged(this); + } +} + +SaveFile * LocalBrowserModel::GetSave() +{ + return stamp; +} + +void LocalBrowserModel::SetSave(SaveFile * newStamp) +{ + if(stamp) + delete stamp; + stamp = new SaveFile(*newStamp); +} + +bool LocalBrowserModel::GetMoveToFront() +{ + return stampToFront; +} + +void LocalBrowserModel::SetMoveToFront(bool move) +{ + stampToFront = move; +} + +void LocalBrowserModel::UpdateSavesList(int pageNumber) +{ + std::vector<SaveFile*> tempSavesList = savesList; + savesList.clear(); + currentPage = pageNumber; + notifyPageChanged(); + notifySavesListChanged(); + //notifyStampsListChanged(); + /*for(int i = 0; i < tempSavesList.size(); i++) + { + delete tempSavesList[i]; + }*/ + + stampIDs = Client::Ref().GetStamps((pageNumber-1)*20, 20); + + for(int i = 0; i<stampIDs.size(); i++) + { + SaveFile * tempSave = Client::Ref().GetStamp(stampIDs[i]); + if(tempSave) + { + savesList.push_back(tempSave); + } + } + notifySavesListChanged(); +} + +void LocalBrowserModel::RescanStamps() +{ + Client::Ref().RescanStamps(); +} + +int LocalBrowserModel::GetPageCount() +{ + return std::max(1, (int)(std::ceil(float(Client::Ref().GetStampsCount())/20.0f))); +} + +void LocalBrowserModel::SelectSave(std::string stampID) +{ + for(int i = 0; i < selected.size(); i++) + { + if(selected[i]==stampID) + { + return; + } + } + selected.push_back(stampID); + notifySelectedChanged(); +} + +void LocalBrowserModel::DeselectSave(std::string stampID) +{ + bool changed = false; +restart: + for(int i = 0; i < selected.size(); i++) + { + if(selected[i]==stampID) + { + selected.erase(selected.begin()+i); + changed = true; + goto restart; //Just ensure all cases are removed. + } + } + if(changed) + notifySelectedChanged(); +} + +void LocalBrowserModel::notifySelectedChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + LocalBrowserView* cObserver = observers[i]; + cObserver->NotifySelectedChanged(this); + } +} + +LocalBrowserModel::~LocalBrowserModel() { + if(stamp) + delete stamp; +} + diff --git a/src/gui/localbrowser/LocalBrowserModel.h b/src/gui/localbrowser/LocalBrowserModel.h new file mode 100644 index 0000000..75167d9 --- /dev/null +++ b/src/gui/localbrowser/LocalBrowserModel.h @@ -0,0 +1,41 @@ +#ifndef STAMPSMODEL_H_ +#define STAMPSMODEL_H_ + +#include <vector> +#include <string> +#include <cmath> + +class SaveFile; + +class LocalBrowserView; +class LocalBrowserModel { + std::vector<std::string> selected; + SaveFile * stamp; + std::vector<std::string> stampIDs; + std::vector<SaveFile*> savesList; + std::vector<LocalBrowserView*> observers; + int currentPage; + bool stampToFront; + void notifySavesListChanged(); + void notifyPageChanged(); + void notifySelectedChanged(); +public: + LocalBrowserModel(); + int GetPageCount(); + int GetPageNum() { return currentPage; } + void AddObserver(LocalBrowserView * observer); + std::vector<SaveFile *> GetSavesList(); + void UpdateSavesList(int pageNumber); + void RescanStamps(); + SaveFile * GetSave(); + void SetSave(SaveFile * newStamp); + bool GetMoveToFront(); + void SetMoveToFront(bool move); + std::vector<std::string> GetSelected() { return selected; } + void ClearSelected() { selected.clear(); notifySelectedChanged(); } + void SelectSave(std::string stampID); + void DeselectSave(std::string stampID); + virtual ~LocalBrowserModel(); +}; + +#endif /* STAMPSMODEL_H_ */ diff --git a/src/gui/localbrowser/LocalBrowserModelException.h b/src/gui/localbrowser/LocalBrowserModelException.h new file mode 100644 index 0000000..5146c53 --- /dev/null +++ b/src/gui/localbrowser/LocalBrowserModelException.h @@ -0,0 +1,16 @@ +#ifndef STAMPSMODELEXCEPTION_H_ +#define STAMPSMODELEXCEPTION_H_ + +#include <string> +#include <exception> +using namespace std; + +class LocalBrowserModelException { + string message; +public: + LocalBrowserModelException(string message_): message(message_) {}; + const char * what() const throw() { return message.c_str(); }; + ~LocalBrowserModelException() throw() {}; +}; + +#endif /* STAMPSMODELEXCEPTION_H_ */ diff --git a/src/gui/localbrowser/LocalBrowserView.cpp b/src/gui/localbrowser/LocalBrowserView.cpp new file mode 100644 index 0000000..a25c281 --- /dev/null +++ b/src/gui/localbrowser/LocalBrowserView.cpp @@ -0,0 +1,216 @@ +#include <sstream> +#include "client/Client.h" +#include "LocalBrowserView.h" + +#include "gui/interface/Button.h" +#include "gui/interface/Textbox.h" +#include "gui/interface/Label.h" +#include "gui/interface/SaveButton.h" +#include "gui/interface/Keys.h" + +#include "gui/dialogues/ErrorMessage.h" +#include "gui/dialogues/ConfirmPrompt.h" +#include "LocalBrowserController.h" +#include "LocalBrowserModel.h" +#include "LocalBrowserModelException.h" + +LocalBrowserView::LocalBrowserView(): + ui::Window(ui::Point(0, 0), ui::Point(XRES+BARSIZE, YRES+MENUSIZE)) +{ + nextButton = new ui::Button(ui::Point(XRES+BARSIZE-52, YRES+MENUSIZE-18), ui::Point(50, 16), "Next \x95"); + previousButton = new ui::Button(ui::Point(1, YRES+MENUSIZE-18), ui::Point(50, 16), "\x96 Prev"); + undeleteButton = new ui::Button(ui::Point(XRES+BARSIZE-122, YRES+MENUSIZE-18), ui::Point(60, 16), "Rescan"); + infoLabel = new ui::Label(ui::Point(51, YRES+MENUSIZE-18), ui::Point(XRES+BARSIZE-102, 16), "Loading..."); + AddComponent(infoLabel); + AddComponent(nextButton); + AddComponent(previousButton); + AddComponent(undeleteButton); + + class NextPageAction : public ui::ButtonAction + { + LocalBrowserView * v; + public: + NextPageAction(LocalBrowserView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->NextPage(); + } + }; + nextButton->SetActionCallback(new NextPageAction(this)); + nextButton->Appearance.HorizontalAlign = ui::Appearance::AlignRight; + nextButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + + class PrevPageAction : public ui::ButtonAction + { + LocalBrowserView * v; + public: + PrevPageAction(LocalBrowserView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->PrevPage(); + } + }; + previousButton->SetActionCallback(new PrevPageAction(this)); + previousButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + previousButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + + class UndeleteAction : public ui::ButtonAction + { + LocalBrowserView * v; + public: + UndeleteAction(LocalBrowserView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->RescanStamps(); + } + }; + undeleteButton->SetActionCallback(new UndeleteAction(this)); + + class RemoveSelectedAction : public ui::ButtonAction + { + LocalBrowserView * v; + public: + RemoveSelectedAction(LocalBrowserView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->RemoveSelected(); + } + }; + + removeSelected = new ui::Button(ui::Point((((XRES+BARSIZE)-100)/2), YRES+MENUSIZE-18), ui::Point(100, 16), "Delete"); + removeSelected->Visible = false; + removeSelected->SetActionCallback(new RemoveSelectedAction(this)); + AddComponent(removeSelected); +} + +void LocalBrowserView::OnTick(float dt) +{ + c->Update(); +} + +void LocalBrowserView::NotifyPageChanged(LocalBrowserModel * sender) +{ + std::stringstream pageInfo; + pageInfo << "Page " << sender->GetPageNum() << " of " << sender->GetPageCount(); + infoLabel->SetText(pageInfo.str()); + if(sender->GetPageNum() == 1) + { + previousButton->Visible = false; + } + else + { + previousButton->Visible = true; + } + if(sender->GetPageNum() == sender->GetPageCount()) + { + nextButton->Visible = false; + } + else + { + nextButton->Visible = true; + } +} + +void LocalBrowserView::NotifySavesListChanged(LocalBrowserModel * sender) +{ + int i = 0; + int buttonWidth, buttonHeight, saveX = 0, saveY = 0, savesX = 5, savesY = 4, buttonPadding = 2; + int buttonAreaWidth, buttonAreaHeight, buttonXOffset, buttonYOffset; + + vector<SaveFile*> saves = sender->GetSavesList(); + for(i = 0; i < stampButtons.size(); i++) + { + RemoveComponent(stampButtons[i]); + delete stampButtons[i]; + } + stampButtons.clear(); + buttonXOffset = 0; + buttonYOffset = 50; + buttonAreaWidth = Size.X; + buttonAreaHeight = Size.Y - buttonYOffset - 18; + buttonWidth = (buttonAreaWidth/savesX) - buttonPadding*2; + buttonHeight = (buttonAreaHeight/savesY) - buttonPadding*2; + class SaveOpenAction: public ui::SaveButtonAction + { + LocalBrowserView * v; + public: + SaveOpenAction(LocalBrowserView * _v) { v = _v; } + virtual void ActionCallback(ui::SaveButton * sender) + { + if(sender->GetSaveFile()) + v->c->OpenSave(sender->GetSaveFile()); + } + virtual void SelectedCallback(ui::SaveButton * sender) + { + if(sender->GetSaveFile()) + v->c->Selected(sender->GetSaveFile()->GetName(), sender->GetSelected()); + } + }; + for(i = 0; i < saves.size(); i++) + { + if(saveX == savesX) + { + if(saveY == savesY-1) + break; + saveX = 0; + saveY++; + } + ui::SaveButton * saveButton; + saveButton = new ui::SaveButton( + ui::Point( + buttonXOffset + buttonPadding + saveX*(buttonWidth+buttonPadding*2), + buttonYOffset + buttonPadding + saveY*(buttonHeight+buttonPadding*2) + ), + ui::Point(buttonWidth, buttonHeight), + saves[i]); + saveButton->SetSelectable(true); + saveButton->SetActionCallback(new SaveOpenAction(this)); + stampButtons.push_back(saveButton); + AddComponent(saveButton); + saveX++; + } +} + +void LocalBrowserView::NotifySelectedChanged(LocalBrowserModel * sender) +{ + vector<std::string> selected = sender->GetSelected(); + for(int j = 0; j < stampButtons.size(); j++) + { + stampButtons[j]->SetSelected(false); + for(int i = 0; i < selected.size(); i++) + { + if(stampButtons[j]->GetSaveFile()->GetName()==selected[i]) + stampButtons[j]->SetSelected(true); + } + } + + if(selected.size()) + { + removeSelected->Visible = true; + } + else + removeSelected->Visible = false; +} + +void LocalBrowserView::OnMouseWheel(int x, int y, int d) +{ + if(!d) + return; + if(d<0) + c->NextPage(); + else + c->PrevPage(); +} +void LocalBrowserView::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + if(key == KEY_ESCAPE) + c->Exit(); + if (key == KEY_CTRL) + c->SetMoveToFront(false); + else + c->SetMoveToFront(true); +} + +LocalBrowserView::~LocalBrowserView() { +} + diff --git a/src/gui/localbrowser/LocalBrowserView.h b/src/gui/localbrowser/LocalBrowserView.h new file mode 100644 index 0000000..02aca28 --- /dev/null +++ b/src/gui/localbrowser/LocalBrowserView.h @@ -0,0 +1,37 @@ +#ifndef STAMPSVIEW_H_ +#define STAMPSVIEW_H_ + +#include <vector> +#include "gui/interface/Window.h" + +namespace ui +{ + class Label; + class Button; + class SaveButton; +} + +class LocalBrowserController; +class LocalBrowserModel; +class LocalBrowserView: public ui::Window { + LocalBrowserController * c; + std::vector<ui::SaveButton*> stampButtons; + ui::Button * undeleteButton; + ui::Button * previousButton; + ui::Button * nextButton; + ui::Label * infoLabel; + ui::Button * removeSelected; +public: + LocalBrowserView(); + //virtual void OnDraw(); + virtual void OnTick(float dt); + void AttachController(LocalBrowserController * c_) { c = c_; }; + void NotifyPageChanged(LocalBrowserModel * sender); + void NotifySavesListChanged(LocalBrowserModel * sender); + void NotifySelectedChanged(LocalBrowserModel * sender); + virtual void OnMouseWheel(int x, int y, int d); + virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + virtual ~LocalBrowserView(); +}; + +#endif /* STAMPSVIEW_H_ */ diff --git a/src/gui/login/LoginController.cpp b/src/gui/login/LoginController.cpp new file mode 100644 index 0000000..fd8e576 --- /dev/null +++ b/src/gui/login/LoginController.cpp @@ -0,0 +1,51 @@ +#include "LoginController.h" +#include "client/User.h" +#include "client/Client.h" + +LoginController::LoginController(ControllerCallback * callback): + HasExited(false) +{ + loginView = new LoginView(); + loginModel = new LoginModel(); + + loginView->AttachController(this); + loginModel->AddObserver(loginView); + + this->callback = callback; + +} + +void LoginController::Login(string username, string password) +{ + loginModel->Login(username, password); +} + +User LoginController::GetUser() +{ + return loginModel->GetUser(); +} + +void LoginController::Exit() +{ + if(ui::Engine::Ref().GetWindow() == loginView) + { + ui::Engine::Ref().CloseWindow(); + } + if(callback) + callback->ControllerExit(); + else + { + Client::Ref().SetAuthUser(loginModel->GetUser()); + } + HasExited = true; +} + +LoginController::~LoginController() { + if(ui::Engine::Ref().GetWindow() == loginView) + { + ui::Engine::Ref().CloseWindow(); + } + delete loginModel; + delete loginView; +} + diff --git a/src/gui/login/LoginController.h b/src/gui/login/LoginController.h new file mode 100644 index 0000000..b723cb9 --- /dev/null +++ b/src/gui/login/LoginController.h @@ -0,0 +1,28 @@ +#ifndef LOGINCONTROLLER_H_ +#define LOGINCONTROLLER_H_ + +#include <string> +#include "LoginView.h" +#include "LoginModel.h" +#include "Controller.h" +#include "client/User.h" + +using namespace std; + +class LoginView; +class LoginModel; +class LoginController { + LoginView * loginView; + LoginModel * loginModel; + ControllerCallback * callback; +public: + bool HasExited; + LoginController(ControllerCallback * callback = NULL); + void Login(string username, string password); + void Exit(); + LoginView * GetView() { return loginView; } + User GetUser(); + virtual ~LoginController(); +}; + +#endif /* LOGINCONTROLLER_H_ */ diff --git a/src/gui/login/LoginModel.cpp b/src/gui/login/LoginModel.cpp new file mode 100644 index 0000000..5b70c2c --- /dev/null +++ b/src/gui/login/LoginModel.cpp @@ -0,0 +1,58 @@ +#include "LoginModel.h" + +LoginModel::LoginModel(): + currentUser(0, "") +{ + +} + +void LoginModel::Login(string username, string password) +{ + statusText = "Logging in..."; + loginStatus = false; + notifyStatusChanged(); + LoginStatus status = Client::Ref().Login(username, password, currentUser); + switch(status) + { + case LoginOkay: + statusText = "Logged in"; + loginStatus = true; + break; + case LoginError: + statusText = "Error: " + Client::Ref().GetLastError(); + break; + } + notifyStatusChanged(); +} + +void LoginModel::AddObserver(LoginView * observer) +{ + observers.push_back(observer); +} + +string LoginModel::GetStatusText() +{ + return statusText; +} + +User LoginModel::GetUser() +{ + return currentUser; +} + +bool LoginModel::GetStatus() +{ + return loginStatus; +} + +void LoginModel::notifyStatusChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyStatusChanged(this); + } +} + +LoginModel::~LoginModel() { +} + diff --git a/src/gui/login/LoginModel.h b/src/gui/login/LoginModel.h new file mode 100644 index 0000000..8149797 --- /dev/null +++ b/src/gui/login/LoginModel.h @@ -0,0 +1,28 @@ +#ifndef LOGINMODEL_H_ +#define LOGINMODEL_H_ + +#include <vector> +#include <string> +#include "LoginView.h" +#include "client/Client.h" + +using namespace std; + +class LoginView; +class LoginModel { + vector<LoginView*> observers; + string statusText; + bool loginStatus; + void notifyStatusChanged(); + User currentUser; +public: + LoginModel(); + void Login(string username, string password); + void AddObserver(LoginView * observer); + string GetStatusText(); + bool GetStatus(); + User GetUser(); + virtual ~LoginModel(); +}; + +#endif /* LOGINMODEL_H_ */ diff --git a/src/gui/login/LoginView.cpp b/src/gui/login/LoginView.cpp new file mode 100644 index 0000000..8ebbeb5 --- /dev/null +++ b/src/gui/login/LoginView.cpp @@ -0,0 +1,148 @@ +#include "LoginView.h" + +#include "gui/interface/Button.h" +#include "gui/interface/Label.h" +#include "gui/interface/Textbox.h" +#include "gui/interface/Keys.h" +#include "gui/Style.h" + +class LoginView::LoginAction : public ui::ButtonAction +{ + LoginView * v; +public: + LoginAction(LoginView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->Login(v->usernameField->GetText(), v->passwordField->GetText()); + } +}; + +class LoginView::CancelAction : public ui::ButtonAction +{ + LoginView * v; +public: + CancelAction(LoginView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->Exit(); + } +}; + +LoginView::LoginView(): + ui::Window(ui::Point(-1, -1), ui::Point(200, 87)), + loginButton(new ui::Button(ui::Point(200-100, 87-17), ui::Point(100, 17), "Sign in")), + cancelButton(new ui::Button(ui::Point(0, 87-17), ui::Point(101, 17), "Sign Out")), + titleLabel(new ui::Label(ui::Point(4, 5), ui::Point(200-16, 16), "Server login")), + usernameField(new ui::Textbox(ui::Point(8, 25), ui::Point(200-16, 17), Client::Ref().GetAuthUser().Username, "[username]")), + passwordField(new ui::Textbox(ui::Point(8, 46), ui::Point(200-16, 17), "", "[password]")), + infoLabel(new ui::Label(ui::Point(8, 67), ui::Point(200-16, 16), "")), + targetSize(0, 0) +{ + targetSize = Size; + FocusComponent(usernameField); + + infoLabel->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; infoLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + infoLabel->Visible = false; + AddComponent(infoLabel); + + AddComponent(loginButton); + SetOkayButton(loginButton); + loginButton->Appearance.HorizontalAlign = ui::Appearance::AlignRight; + loginButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + loginButton->Appearance.TextInactive = style::Colour::ConfirmButton; + loginButton->SetActionCallback(new LoginAction(this)); + AddComponent(cancelButton); + cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + cancelButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + cancelButton->SetActionCallback(new CancelAction(this)); + AddComponent(titleLabel); + titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + + AddComponent(usernameField); + usernameField->Appearance.icon = IconUsername; + usernameField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; usernameField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(passwordField); + passwordField->Appearance.icon = IconPassword; + passwordField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; passwordField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + passwordField->SetHidden(true); +} + +void LoginView::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + switch(key) + { + case KEY_TAB: + if(IsFocused(usernameField)) + FocusComponent(passwordField); + else + FocusComponent(usernameField); + break; + } +} + +void LoginView::OnTryExit(ExitMethod method) +{ + ui::Engine::Ref().CloseWindow(); +} + +void LoginView::NotifyStatusChanged(LoginModel * sender) +{ + if(!infoLabel->GetText().length() && sender->GetStatusText().length()) + { + targetSize = Size+ui::Point(0, 18); + infoLabel->Visible = true; + } + infoLabel->SetText(sender->GetStatusText()); + if(sender->GetStatus()) + { + c->Exit(); + } +} + +void LoginView::OnTick(float dt) +{ + //if(targetSize != Size) + { + ui::Point difference = targetSize-Size; + if(difference.X!=0) + { + int xdiff = difference.X/5; + if(xdiff == 0) + xdiff = 1*isign(difference.X); + Size.X += xdiff; + } + if(difference.Y!=0) + { + int ydiff = difference.Y/5; + if(ydiff == 0) + ydiff = 1*isign(difference.Y); + Size.Y += ydiff; + } + + loginButton->Position.Y = Size.Y-17; + cancelButton->Position.Y = Size.Y-17; + } +} + +void LoginView::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); +} + +LoginView::~LoginView() { + RemoveComponent(titleLabel); + RemoveComponent(loginButton); + RemoveComponent(cancelButton); + RemoveComponent(usernameField); + RemoveComponent(passwordField); + RemoveComponent(infoLabel); + delete cancelButton; + delete loginButton; + delete titleLabel; + delete usernameField; + delete passwordField; + delete infoLabel; +} + diff --git a/src/gui/login/LoginView.h b/src/gui/login/LoginView.h new file mode 100644 index 0000000..adbe4ec --- /dev/null +++ b/src/gui/login/LoginView.h @@ -0,0 +1,39 @@ +#ifndef LOGINVIEW_H_ +#define LOGINVIEW_H_ + +#include "gui/interface/Window.h" +#include "LoginController.h" +#include "LoginModel.h" + +namespace ui +{ + class Textbox; + class Button; + class Label; +} + +class LoginController; +class LoginMode; +class LoginView: public ui::Window { + LoginController * c; + ui::Point targetSize; + ui::Button * loginButton; + ui::Button * cancelButton; + ui::Label * titleLabel; + ui::Label * infoLabel; + ui::Textbox * usernameField; + ui::Textbox * passwordField; +public: + class LoginAction; + class CancelAction; + LoginView(); + virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + virtual void OnTryExit(ExitMethod method); + void AttachController(LoginController * c_) { c = c_; } + void NotifyStatusChanged(LoginModel * sender); + virtual void OnDraw(); + virtual void OnTick(float dt); + virtual ~LoginView(); +}; + +#endif /* LOGINVIEW_H_ */ diff --git a/src/gui/options/OptionsController.cpp b/src/gui/options/OptionsController.cpp new file mode 100644 index 0000000..672f2fc --- /dev/null +++ b/src/gui/options/OptionsController.cpp @@ -0,0 +1,111 @@ +#include "OptionsController.h" +#include "gui/dialogues/ErrorMessage.h" + +OptionsController::OptionsController(GameModel * gModel_, ControllerCallback * callback_): + callback(callback_), + gModel(gModel_), + HasExited(false) +{ + view = new OptionsView(); + model = new OptionsModel(gModel); + model->AddObserver(view); + + view->AttachController(this); + +} + +void OptionsController::SetHeatSimulation(bool state) +{ + model->SetHeatSimulation(state); +} + +void OptionsController::SetAmbientHeatSimulation(bool state) +{ + model->SetAmbientHeatSimulation(state); +} + +void OptionsController::SetNewtonianGravity(bool state) +{ + model->SetNewtonianGravity(state); +} + +void OptionsController::SetWaterEqualisation(bool state) +{ + model->SetWaterEqualisation(state); +} + +void OptionsController::SetGravityMode(int gravityMode) +{ + model->SetGravityMode(gravityMode); +} + +void OptionsController::SetAirMode(int airMode) +{ + model->SetAirMode(airMode); +} + +void OptionsController::SetEdgeMode(int airMode) +{ + model->SetEdgeMode(airMode); +} + +void OptionsController::SetFullscreen(bool fullscreen) +{ + model->SetFullscreen(fullscreen); +} + +void OptionsController::SetShowAvatars(bool showAvatars) +{ + model->SetShowAvatars(showAvatars); +} + +void OptionsController::SetScale(bool scale) +{ + if(scale) + { + if(ui::Engine::Ref().GetMaxWidth() >= ui::Engine::Ref().GetWidth() * 2 && ui::Engine::Ref().GetMaxHeight() >= ui::Engine::Ref().GetHeight() * 2) + model->SetScale(scale); + else + { + new ErrorMessage("Screen resolution error", "Your screen size is too small to use this scale mode."); + model->SetScale(false); + } + } + else + model->SetScale(scale); + +} + +void OptionsController::SetFastQuit(bool fastquit) +{ + model->SetFastQuit(fastquit); +} + +OptionsView * OptionsController::GetView() +{ + return view; +} + +void OptionsController::Exit() +{ + if(ui::Engine::Ref().GetWindow() == view) + { + ui::Engine::Ref().CloseWindow(); + } + if(callback) + callback->ControllerExit(); + HasExited = true; +} + + +OptionsController::~OptionsController() { + if(ui::Engine::Ref().GetWindow() == view) + { + ui::Engine::Ref().CloseWindow(); + } + delete model; + delete view; + if(callback) + delete callback; +} + diff --git a/src/gui/options/OptionsController.h b/src/gui/options/OptionsController.h new file mode 100644 index 0000000..481f9d2 --- /dev/null +++ b/src/gui/options/OptionsController.h @@ -0,0 +1,36 @@ +#ifndef OPTIONSCONTROLLER_H_ +#define OPTIONSCONTROLLER_H_ + +#include "Controller.h" +#include "simulation/Simulation.h" +#include "OptionsView.h" +#include "OptionsModel.h" + +class GameModel; +class OptionsModel; +class OptionsView; +class OptionsController { + GameModel * gModel; + OptionsView * view; + OptionsModel * model; + ControllerCallback * callback; +public: + bool HasExited; + OptionsController(GameModel * gModel_, ControllerCallback * callback_); + void SetHeatSimulation(bool state); + void SetAmbientHeatSimulation(bool state); + void SetNewtonianGravity(bool state); + void SetWaterEqualisation(bool state); + void SetGravityMode(int gravityMode); + void SetAirMode(int airMode); + void SetEdgeMode(int airMode); + void SetFullscreen(bool fullscreen); + void SetScale(bool scale); + void SetFastQuit(bool fastquit); + void SetShowAvatars(bool showAvatars); + void Exit(); + OptionsView * GetView(); + virtual ~OptionsController(); +}; + +#endif /* OPTIONSCONTROLLER_H_ */ diff --git a/src/gui/options/OptionsModel.cpp b/src/gui/options/OptionsModel.cpp new file mode 100644 index 0000000..829bf0c --- /dev/null +++ b/src/gui/options/OptionsModel.cpp @@ -0,0 +1,148 @@ +#include "simulation/Air.h" +#include "gui/game/GameModel.h" +#include "OptionsModel.h" + +OptionsModel::OptionsModel(GameModel * gModel_) { + gModel = gModel_; + sim = gModel->GetSimulation(); +} + +void OptionsModel::AddObserver(OptionsView* view) +{ + observers.push_back(view); + view->NotifySettingsChanged(this); +} + +bool OptionsModel::GetHeatSimulation() +{ + return sim->legacy_enable?false:true; +} + +void OptionsModel::SetHeatSimulation(bool state) +{ + sim->legacy_enable = state?0:1; + notifySettingsChanged(); +} + +bool OptionsModel::GetAmbientHeatSimulation() +{ + return sim->aheat_enable?true:false; +} + +void OptionsModel::SetAmbientHeatSimulation(bool state) +{ + sim->aheat_enable = state?1:0; + notifySettingsChanged(); +} + +bool OptionsModel::GetNewtonianGravity() +{ + return sim->grav->ngrav_enable?true:false; +} + +void OptionsModel::SetNewtonianGravity(bool state) +{ + if(state) + sim->grav->start_grav_async(); + else + sim->grav->stop_grav_async(); + notifySettingsChanged(); +} + +bool OptionsModel::GetWaterEqualisation() +{ + return sim->water_equal_test?true:false; +} + +void OptionsModel::SetWaterEqualisation(bool state) +{ + sim->water_equal_test = state?1:0; + notifySettingsChanged(); +} + +int OptionsModel::GetAirMode() +{ + return sim->air->airMode; +} +void OptionsModel::SetAirMode(int airMode) +{ + sim->air->airMode = airMode; + notifySettingsChanged(); +} + +int OptionsModel::GetEdgeMode() +{ + return gModel->GetEdgeMode(); +} +void OptionsModel::SetEdgeMode(int edgeMode) +{ + gModel->SetEdgeMode(edgeMode); + notifySettingsChanged(); +} + +int OptionsModel::GetGravityMode() +{ + return sim->gravityMode; +} +void OptionsModel::SetGravityMode(int gravityMode) +{ + sim->gravityMode = gravityMode; + notifySettingsChanged(); +} + +bool OptionsModel::GetScale() +{ + return ui::Engine::Ref().GetScale()==2; +} +void OptionsModel::SetScale(bool doubleScale) +{ + ui::Engine::Ref().SetScale(doubleScale?2:1); + Client::Ref().SetPref("Scale", int(doubleScale?2:1)); + notifySettingsChanged(); +} + + +bool OptionsModel::GetFullscreen() +{ + return ui::Engine::Ref().GetFullscreen(); +} +void OptionsModel::SetFullscreen(bool fullscreen) +{ + ui::Engine::Ref().SetFullscreen(fullscreen); + Client::Ref().SetPref("Fullscreen", bool(fullscreen)); + notifySettingsChanged(); +} + +bool OptionsModel::GetFastQuit() +{ + return ui::Engine::Ref().GetFastQuit(); +} +void OptionsModel::SetFastQuit(bool fastquit) +{ + ui::Engine::Ref().SetFastQuit(fastquit); + Client::Ref().SetPref("FastQuit", bool(fastquit)); + notifySettingsChanged(); +} + +bool OptionsModel::GetShowAvatars() +{ + return Client::Ref().GetPrefBool("ShowAvatars", true); +} + +void OptionsModel::SetShowAvatars(bool state) +{ + Client::Ref().SetPref("ShowAvatars", state); + notifySettingsChanged(); +} + +void OptionsModel::notifySettingsChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifySettingsChanged(this); + } +} + +OptionsModel::~OptionsModel() { +} + diff --git a/src/gui/options/OptionsModel.h b/src/gui/options/OptionsModel.h new file mode 100644 index 0000000..1fdf372 --- /dev/null +++ b/src/gui/options/OptionsModel.h @@ -0,0 +1,43 @@ +#ifndef OPTIONSMODEL_H_ +#define OPTIONSMODEL_H_ +#include <vector> +#include "OptionsView.h" +#include "simulation/Simulation.h" + +class GameModel; +class Simulation; +class OptionsView; +class OptionsModel { + GameModel * gModel; + Simulation * sim; + std::vector<OptionsView*> observers; + void notifySettingsChanged(); +public: + OptionsModel(GameModel * gModel); + void AddObserver(OptionsView* view); + bool GetHeatSimulation(); + void SetHeatSimulation(bool state); + bool GetAmbientHeatSimulation(); + void SetAmbientHeatSimulation(bool state); + bool GetNewtonianGravity(); + void SetNewtonianGravity(bool state); + bool GetWaterEqualisation(); + void SetWaterEqualisation(bool state); + bool GetShowAvatars(); + void SetShowAvatars(bool state); + int GetAirMode(); + void SetAirMode(int airMode); + int GetEdgeMode(); + void SetEdgeMode(int edgeMode); + int GetGravityMode(); + void SetGravityMode(int gravityMode); + bool GetFullscreen(); + void SetFullscreen(bool fullscreen); + bool GetFastQuit(); + void SetFastQuit(bool fastquit); + bool GetScale(); + void SetScale(bool scale); + virtual ~OptionsModel(); +}; + +#endif /* OPTIONSMODEL_H_ */ diff --git a/src/gui/options/OptionsView.cpp b/src/gui/options/OptionsView.cpp new file mode 100644 index 0000000..71c86aa --- /dev/null +++ b/src/gui/options/OptionsView.cpp @@ -0,0 +1,248 @@ +#include "OptionsView.h" +#include "gui/Style.h" +#include "gui/interface/Button.h" +#include "gui/interface/Label.h" +#include "gui/interface/DropDown.h" + +OptionsView::OptionsView(): + ui::Window(ui::Point(-1, -1), ui::Point(300, 310)){ + + ui::Label * tempLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 14), "Simulation Options"); + tempLabel->SetTextColour(style::Colour::InformationTitle); + tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(tempLabel); + + class HeatSimulationAction: public ui::CheckboxAction + { + OptionsView * v; + public: + HeatSimulationAction(OptionsView * v_){ v = v_; } + virtual void ActionCallback(ui::Checkbox * sender){ v->c->SetHeatSimulation(sender->GetChecked()); } + }; + + heatSimulation = new ui::Checkbox(ui::Point(8, 23), ui::Point(Size.X-6, 16), "Heat simulation \bgIntroduced in version 34", ""); + heatSimulation->SetActionCallback(new HeatSimulationAction(this)); + AddComponent(heatSimulation); + tempLabel = new ui::Label(ui::Point(24, heatSimulation->Position.Y+14), ui::Point(Size.X-28, 16), "\bgCan cause odd behaviour with very old saves"); + tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(tempLabel); + + class AmbientHeatSimulationAction: public ui::CheckboxAction + { + OptionsView * v; + public: + AmbientHeatSimulationAction(OptionsView * v_){ v = v_; } + virtual void ActionCallback(ui::Checkbox * sender){ v->c->SetAmbientHeatSimulation(sender->GetChecked()); } + }; + + ambientHeatSimulation = new ui::Checkbox(ui::Point(8, 53), ui::Point(Size.X-6, 16), "Ambient heat simulation \bgIntroduced in version 50", ""); + ambientHeatSimulation->SetActionCallback(new AmbientHeatSimulationAction(this)); + AddComponent(ambientHeatSimulation); + tempLabel = new ui::Label(ui::Point(24, ambientHeatSimulation->Position.Y+14), ui::Point(Size.X-28, 16), "\bgCan cause odd behaviour with old saves"); + tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(tempLabel); + + class NewtonianGravityAction: public ui::CheckboxAction + { + OptionsView * v; + public: + NewtonianGravityAction(OptionsView * v_){ v = v_; } + virtual void ActionCallback(ui::Checkbox * sender){ v->c->SetNewtonianGravity(sender->GetChecked()); } + }; + + newtonianGravity = new ui::Checkbox(ui::Point(8, 83), ui::Point(Size.X-6, 16), "Newtonian gravity \bgIntroduced in version 48", ""); + newtonianGravity->SetActionCallback(new NewtonianGravityAction(this)); + AddComponent(newtonianGravity); + tempLabel = new ui::Label(ui::Point(24, newtonianGravity->Position.Y+14), ui::Point(Size.X-28, 16), "\bgMay cause poor performance on older computers"); + tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(tempLabel); + + class WaterEqualisationAction: public ui::CheckboxAction + { + OptionsView * v; + public: + WaterEqualisationAction(OptionsView * v_){ v = v_; } + virtual void ActionCallback(ui::Checkbox * sender){ v->c->SetWaterEqualisation(sender->GetChecked()); } + }; + + waterEqualisation = new ui::Checkbox(ui::Point(8, 113), ui::Point(Size.X-6, 16), "Water equalisation \bgIntroduced in version 61", ""); + waterEqualisation->SetActionCallback(new WaterEqualisationAction(this)); + AddComponent(waterEqualisation); + tempLabel = new ui::Label(ui::Point(24, waterEqualisation->Position.Y+14), ui::Point(Size.X-28, 16), "\bgMay cause poor performance with a lot of water"); + tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(tempLabel); + + class AirModeChanged: public ui::DropDownAction + { + OptionsView * v; + public: + AirModeChanged(OptionsView * v): v(v) { } + virtual void OptionChanged(ui::DropDown * sender, std::pair<std::string, int> option) { v->c->SetAirMode(option.second); } + }; + airMode = new ui::DropDown(ui::Point(Size.X-88, 146), ui::Point(80, 16)); + AddComponent(airMode); + airMode->AddOption(std::pair<std::string, int>("On", 0)); + airMode->AddOption(std::pair<std::string, int>("Pressure off", 1)); + airMode->AddOption(std::pair<std::string, int>("Velocity off", 2)); + airMode->AddOption(std::pair<std::string, int>("Off", 3)); + airMode->AddOption(std::pair<std::string, int>("No Update", 4)); + airMode->SetActionCallback(new AirModeChanged(this)); + + tempLabel = new ui::Label(ui::Point(8, 146), ui::Point(Size.X-96, 16), "Air Simulation Mode"); + tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(tempLabel); + + class GravityModeChanged: public ui::DropDownAction + { + OptionsView * v; + public: + GravityModeChanged(OptionsView * v): v(v) { } + virtual void OptionChanged(ui::DropDown * sender, std::pair<std::string, int> option) { v->c->SetGravityMode(option.second); } + }; + + gravityMode = new ui::DropDown(ui::Point(Size.X-88, 166), ui::Point(80, 16)); + AddComponent(gravityMode); + gravityMode->AddOption(std::pair<std::string, int>("Vertical", 0)); + gravityMode->AddOption(std::pair<std::string, int>("Off", 1)); + gravityMode->AddOption(std::pair<std::string, int>("Radial", 2)); + gravityMode->SetActionCallback(new GravityModeChanged(this)); + + tempLabel = new ui::Label(ui::Point(8, 166), ui::Point(Size.X-96, 16), "Gravity Simulation Mode"); + tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(tempLabel); + + class EdgeModeChanged: public ui::DropDownAction + { + OptionsView * v; + public: + EdgeModeChanged(OptionsView * v): v(v) { } + virtual void OptionChanged(ui::DropDown * sender, std::pair<std::string, int> option) { v->c->SetEdgeMode(option.second); } + }; + + edgeMode = new ui::DropDown(ui::Point(Size.X-88, 186), ui::Point(80, 16)); + AddComponent(edgeMode); + edgeMode->AddOption(std::pair<std::string, int>("Void", 0)); + edgeMode->AddOption(std::pair<std::string, int>("Solid", 1)); + edgeMode->SetActionCallback(new EdgeModeChanged(this)); + + tempLabel = new ui::Label(ui::Point(8, 186), ui::Point(Size.X-96, 16), "Edge Mode"); + tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(tempLabel); + + class ScaleAction: public ui::CheckboxAction + { + OptionsView * v; + public: + ScaleAction(OptionsView * v_){ v = v_; } + virtual void ActionCallback(ui::Checkbox * sender){ v->c->SetScale(sender->GetChecked()); } + }; + + scale = new ui::Checkbox(ui::Point(8, 210), ui::Point(Size.X-6, 16), "Large screen", ""); + scale->SetActionCallback(new ScaleAction(this)); + tempLabel = new ui::Label(ui::Point(scale->Position.X+Graphics::textwidth(scale->GetText().c_str())+20, scale->Position.Y), ui::Point(Size.X-28, 16), "\bg- Double window size for smaller screen"); + tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(tempLabel); + AddComponent(scale); + + + class FullscreenAction: public ui::CheckboxAction + { + OptionsView * v; + public: + FullscreenAction(OptionsView * v_){ v = v_; } + virtual void ActionCallback(ui::Checkbox * sender){ v->c->SetFullscreen(sender->GetChecked()); } + }; + + fullscreen = new ui::Checkbox(ui::Point(8, 230), ui::Point(Size.X-6, 16), "Fullscreen", ""); + fullscreen->SetActionCallback(new FullscreenAction(this)); + tempLabel = new ui::Label(ui::Point(fullscreen->Position.X+Graphics::textwidth(fullscreen->GetText().c_str())+20, fullscreen->Position.Y), ui::Point(Size.X-28, 16), "\bg- Use the entire screen"); + tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(tempLabel); + AddComponent(fullscreen); + + + class FastQuitAction: public ui::CheckboxAction + { + OptionsView * v; + public: + FastQuitAction(OptionsView * v_){ v = v_; } + virtual void ActionCallback(ui::Checkbox * sender){ v->c->SetFastQuit(sender->GetChecked()); } + }; + + fastquit = new ui::Checkbox(ui::Point(8, 250), ui::Point(Size.X-6, 16), "Fast Quit", ""); + fastquit->SetActionCallback(new FastQuitAction(this)); + tempLabel = new ui::Label(ui::Point(fastquit->Position.X+Graphics::textwidth(fastquit->GetText().c_str())+20, fastquit->Position.Y), ui::Point(Size.X-28, 16), "\bg- Always exit completely when hitting close"); + tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(tempLabel); + AddComponent(fastquit); + + class ShowAvatarsAction: public ui::CheckboxAction + { + OptionsView * v; + public: + ShowAvatarsAction(OptionsView * v_){ v = v_; } + virtual void ActionCallback(ui::Checkbox * sender){ v->c->SetShowAvatars(sender->GetChecked()); } + }; + + showAvatars = new ui::Checkbox(ui::Point(8, 270), ui::Point(Size.X-6, 16), "Show Avatars", ""); + showAvatars->SetActionCallback(new ShowAvatarsAction(this)); + tempLabel = new ui::Label(ui::Point(showAvatars->Position.X+Graphics::textwidth(showAvatars->GetText().c_str())+20, showAvatars->Position.Y), ui::Point(Size.X-28, 16), "\bg- Disable if you have a slow connection"); + tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(tempLabel); + AddComponent(showAvatars); + + class CloseAction: public ui::ButtonAction + { + public: + OptionsView * v; + CloseAction(OptionsView * v_) { v = v_; } + void ActionCallback(ui::Button * sender) + { + v->c->Exit(); + } + }; + + ui::Button * tempButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "OK"); + tempButton->SetActionCallback(new CloseAction(this)); + AddComponent(tempButton); + SetCancelButton(tempButton); + SetOkayButton(tempButton); +} + +void OptionsView::NotifySettingsChanged(OptionsModel * sender) +{ + heatSimulation->SetChecked(sender->GetHeatSimulation()); + ambientHeatSimulation->SetChecked(sender->GetAmbientHeatSimulation()); + newtonianGravity->SetChecked(sender->GetNewtonianGravity()); + waterEqualisation->SetChecked(sender->GetWaterEqualisation()); + airMode->SetOption(sender->GetAirMode()); + gravityMode->SetOption(sender->GetGravityMode()); + edgeMode->SetOption(sender->GetEdgeMode()); + scale->SetChecked(sender->GetScale()); + fullscreen->SetChecked(sender->GetFullscreen()); + fastquit->SetChecked(sender->GetFastQuit()); + showAvatars->SetChecked(sender->GetShowAvatars()); +} + +void OptionsView::AttachController(OptionsController * c_) +{ + c = c_; +} + +void OptionsView::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + g->draw_line(Position.X+1, Position.Y+scale->Position.Y-4, Position.X+Size.X-1, Position.Y+scale->Position.Y-4, 255, 255, 255, 180); +} + +void OptionsView::OnTryExit(ExitMethod method) +{ + c->Exit(); +} + + +OptionsView::~OptionsView() { +} + diff --git a/src/gui/options/OptionsView.h b/src/gui/options/OptionsView.h new file mode 100644 index 0000000..2bbc3f2 --- /dev/null +++ b/src/gui/options/OptionsView.h @@ -0,0 +1,34 @@ +#ifndef OPTIONSVIEW_H_ +#define OPTIONSVIEW_H_ + +#include "gui/interface/Window.h" +#include "OptionsController.h" +#include "gui/interface/Checkbox.h" +#include "gui/interface/DropDown.h" +#include "OptionsModel.h" + +class OptionsModel; +class OptionsController; +class OptionsView: public ui::Window { + OptionsController * c; + ui::Checkbox * heatSimulation; + ui::Checkbox * ambientHeatSimulation; + ui::Checkbox * newtonianGravity; + ui::Checkbox * waterEqualisation; + ui::DropDown * airMode; + ui::DropDown * gravityMode; + ui::DropDown * edgeMode; + ui::Checkbox * scale; + ui::Checkbox * fullscreen; + ui::Checkbox * fastquit; + ui::Checkbox * showAvatars; +public: + OptionsView(); + void NotifySettingsChanged(OptionsModel * sender); + void AttachController(OptionsController * c_); + void OnDraw(); + void OnTryExit(ExitMethod method); + virtual ~OptionsView(); +}; + +#endif /* OPTIONSVIEW_H_ */ diff --git a/src/gui/preview/Comment.h b/src/gui/preview/Comment.h new file mode 100644 index 0000000..1d84042 --- /dev/null +++ b/src/gui/preview/Comment.h @@ -0,0 +1,28 @@ +#ifndef COMMENT_H_ +#define COMMENT_H_ + +#include <string> + +class SaveComment +{ +public: + int authorID; + std::string authorName; + std::string authorNameFormatted; + std::string comment; + SaveComment(int userID, std::string username, std::string usernameFormatted, std::string commentText): + authorID(userID), authorName(username), authorNameFormatted(usernameFormatted), comment(commentText) + { + } + SaveComment(const SaveComment & comment): + authorID(comment.authorID), authorName(comment.authorName), authorNameFormatted(comment.authorNameFormatted), comment(comment.comment) + { + } + SaveComment(const SaveComment * comment): + authorID(comment->authorID), authorName(comment->authorName), authorNameFormatted(comment->authorNameFormatted), comment(comment->comment) + { + } +}; + + +#endif /* COMMENT_H_ */ diff --git a/src/gui/preview/PreviewController.cpp b/src/gui/preview/PreviewController.cpp new file mode 100644 index 0000000..30622c2 --- /dev/null +++ b/src/gui/preview/PreviewController.cpp @@ -0,0 +1,192 @@ +#include <sstream> +#include "client/Client.h" +#include "PreviewController.h" +#include "PreviewView.h" +#include "PreviewModel.h" +#include "PreviewModelException.h" +#include "gui/dialogues/ErrorMessage.h" +#include "gui/login/LoginController.h" +#include "Controller.h" + +PreviewController::PreviewController(int saveID, int saveDate, ControllerCallback * callback): + HasExited(false), + saveId(saveID), + saveDate(saveDate), + loginWindow(NULL) +{ + previewModel = new PreviewModel(); + previewView = new PreviewView(); + previewModel->AddObserver(previewView); + previewView->AttachController(this); + + previewModel->UpdateSave(saveID, saveDate); + + if(Client::Ref().GetAuthUser().ID) + { + previewModel->SetCommentBoxEnabled(true); + } + + Client::Ref().AddListener(this); + + this->callback = callback; +} + +PreviewController::PreviewController(int saveID, ControllerCallback * callback): + HasExited(false), + saveId(saveID), + saveDate(0), + loginWindow(NULL) +{ + previewModel = new PreviewModel(); + previewView = new PreviewView(); + previewModel->AddObserver(previewView); + previewView->AttachController(this); + + previewModel->UpdateSave(saveID, 0); + + if(Client::Ref().GetAuthUser().ID) + { + previewModel->SetCommentBoxEnabled(true); + } + + Client::Ref().AddListener(this); + + this->callback = callback; +} + +void PreviewController::Update() +{ + if(loginWindow && loginWindow->HasExited == true) + { + delete loginWindow; + loginWindow = NULL; + } + + try + { + previewModel->Update(); + } + catch (PreviewModelException & e) + { + Exit(); + new ErrorMessage("Error", e.what()); + } + if(previewModel->GetDoOpen() && previewModel->GetSave() && previewModel->GetSave()->GetGameSave()) + { + Exit(); + } +} + +bool PreviewController::SubmitComment(std::string comment) +{ + if(comment.length() < 4) + { + new ErrorMessage("Error", "Comment is too short"); + return false; + } + else + { + RequestStatus status = Client::Ref().AddComment(saveId, comment); + if(status != RequestOkay) + { + new ErrorMessage("Error Submitting comment", Client::Ref().GetLastError()); + return false; + } + else + { + previewModel->UpdateComments(1); + } + } + return true; +} + +void PreviewController::ShowLogin() +{ + loginWindow = new LoginController(); + ui::Engine::Ref().ShowWindow(loginWindow->GetView()); +} + +void PreviewController::NotifyAuthUserChanged(Client * sender) +{ + previewModel->SetCommentBoxEnabled(sender->GetAuthUser().ID); +} + +SaveInfo * PreviewController::GetSave() +{ + return previewModel->GetSave(); +} + +bool PreviewController::GetDoOpen() +{ + return previewModel->GetDoOpen(); +} + +void PreviewController::DoOpen() +{ + previewModel->SetDoOpen(true); +} + +void PreviewController::Report(std::string message) +{ + if(Client::Ref().ReportSave(saveId, message) == RequestOkay) + { + Exit(); + new ErrorMessage("Information", "Report submitted"); //TODO: InfoMessage + } + else + new ErrorMessage("Error", "Unable file report"); +} + +void PreviewController::FavouriteSave() +{ + if(previewModel->GetSave() && Client::Ref().GetAuthUser().ID) + { + if(previewModel->GetSave()->Favourite) + previewModel->SetFavourite(false); + else + previewModel->SetFavourite(true); + } +} + +void PreviewController::OpenInBrowser() +{ + std::stringstream uriStream; + uriStream << "http://" << SERVER << "/Browse/View.html?ID=" << saveId; + OpenURI(uriStream.str()); +} + +void PreviewController::NextCommentPage() +{ + if(previewModel->GetCommentsPageNum() < previewModel->GetCommentsPageCount() && previewModel->GetCommentsLoaded()) + previewModel->UpdateComments(previewModel->GetCommentsPageNum()+1); +} + +void PreviewController::PrevCommentPage() +{ + if(previewModel->GetCommentsPageNum()>1 && previewModel->GetCommentsLoaded()) + previewModel->UpdateComments(previewModel->GetCommentsPageNum()-1); +} + +void PreviewController::Exit() +{ + if(ui::Engine::Ref().GetWindow() == previewView) + { + ui::Engine::Ref().CloseWindow(); + } + HasExited = true; + if(callback) + callback->ControllerExit(); +} + +PreviewController::~PreviewController() { + if(ui::Engine::Ref().GetWindow() == previewView) + { + ui::Engine::Ref().CloseWindow(); + } + Client::Ref().RemoveListener(this); + delete previewModel; + delete previewView; + if(callback) + delete callback; +} + diff --git a/src/gui/preview/PreviewController.h b/src/gui/preview/PreviewController.h new file mode 100644 index 0000000..e1e429d --- /dev/null +++ b/src/gui/preview/PreviewController.h @@ -0,0 +1,45 @@ +#ifndef PREVIEWCONTROLLER_H_ +#define PREVIEWCONTROLLER_H_ + +#include "gui/preview/PreviewModel.h" +#include "gui/preview/PreviewView.h" +#include "Controller.h" +#include "client/SaveInfo.h" +#include "client/ClientListener.h" + +class LoginController; +class PreviewModel; +class PreviewView; +class PreviewController: public ClientListener { + int saveId; + int saveDate; + PreviewModel * previewModel; + PreviewView * previewView; + LoginController * loginWindow; + ControllerCallback * callback; +public: + virtual void NotifyAuthUserChanged(Client * sender); + inline int SaveID() { return saveId; }; + + bool HasExited; + PreviewController(int saveID, ControllerCallback * callback); + PreviewController(int saveID, int saveDate, ControllerCallback * callback); + void Exit(); + void DoOpen(); + void OpenInBrowser(); + void Report(std::string message); + void ShowLogin(); + bool GetDoOpen(); + SaveInfo * GetSave(); + PreviewView * GetView() { return previewView; } + void Update(); + void FavouriteSave(); + bool SubmitComment(std::string comment); + + void NextCommentPage(); + void PrevCommentPage(); + + virtual ~PreviewController(); +}; + +#endif /* PREVIEWCONTROLLER_H_ */ diff --git a/src/gui/preview/PreviewModel.cpp b/src/gui/preview/PreviewModel.cpp new file mode 100644 index 0000000..8d608cf --- /dev/null +++ b/src/gui/preview/PreviewModel.cpp @@ -0,0 +1,360 @@ +#include <cmath> +#include "PreviewModel.h" +#include "client/Client.h" +#include "client/GameSave.h"; +#include "PreviewModelException.h" + +PreviewModel::PreviewModel(): + save(NULL), + saveComments(NULL), + doOpen(false), + updateSaveDataWorking(false), + updateSaveDataFinished(false), + updateSaveInfoWorking(false), + updateSaveInfoFinished(false), + updateSaveCommentsWorking(false), + updateSaveCommentsFinished(false), + commentsTotal(0), + commentsPageNumber(1), + commentBoxEnabled(false) +{ + +} + +void * PreviewModel::updateSaveInfoTHelper(void * obj) +{ + return ((PreviewModel*)obj)->updateSaveInfoT(); +} + +void * PreviewModel::updateSaveDataTHelper(void * obj) +{ + return ((PreviewModel*)obj)->updateSaveDataT(); +} + +void * PreviewModel::updateSaveCommentsTHelper(void * obj) +{ + return ((PreviewModel*)obj)->updateSaveCommentsT(); +} + +void PreviewModel::updateSaveInfoTDelete(void * arg) +{ + delete arg; +} +void PreviewModel::updateSaveDataTDelete(void * arg) +{ + free(arg); +} +void PreviewModel::updateSaveCommentsTDelete(void * arg) +{ + for(int i = 0; i < ((std::vector<SaveComment*> *)arg)->size(); i++) + delete ((std::vector<SaveComment*> *)arg)->at(i); + ((std::vector<SaveComment*> *)arg)->clear(); + delete arg; +} + +void * PreviewModel::updateSaveInfoT() +{ + SaveInfo * tempSave; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); + tempSave = Client::Ref().GetSave(tSaveID, tSaveDate); + pthread_cleanup_push(&updateSaveInfoTDelete,tempSave); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); + pthread_testcancel(); + updateSaveInfoFinished = true; + pthread_cleanup_pop(0); + return tempSave; +} + +void * PreviewModel::updateSaveDataT() +{ + int tempDataSize; + unsigned char * tempData; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); + tempData = Client::Ref().GetSaveData(tSaveID, tSaveDate, tempDataSize); + pthread_cleanup_push(&updateSaveDataTDelete,tempData); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); + pthread_testcancel(); + saveDataBuffer.clear(); + if (tempData) + saveDataBuffer.insert(saveDataBuffer.begin(), tempData, tempData+tempDataSize); + updateSaveDataFinished = true; + pthread_cleanup_pop(1); + return NULL; +} + +void * PreviewModel::updateSaveCommentsT() +{ + std::vector<SaveComment*> * tempComments; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); + tempComments = Client::Ref().GetComments(tSaveID, (commentsPageNumber-1)*20, 20); + pthread_cleanup_push(&updateSaveCommentsTDelete,tempComments); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); + pthread_testcancel(); + updateSaveCommentsFinished = true; + pthread_cleanup_pop(0); + return tempComments; +} + +void PreviewModel::SetFavourite(bool favourite) +{ + if(save) + { + Client::Ref().FavouriteSave(save->id, favourite); + save->Favourite = favourite; + notifySaveChanged(); + } +} + +bool PreviewModel::GetCommentBoxEnabled() +{ + return commentBoxEnabled; +} + +void PreviewModel::SetCommentBoxEnabled(bool enabledState) +{ + if(enabledState != commentBoxEnabled) + { + commentBoxEnabled = enabledState; + notifyCommentBoxEnabledChanged(); + } +} + +void PreviewModel::UpdateSave(int saveID, int saveDate) +{ + this->tSaveID = saveID; + this->tSaveDate = saveDate; + + if(save) + { + delete save; + save = NULL; + } + saveDataBuffer.clear(); + if(saveComments) + { + for(int i = 0; i < saveComments->size(); i++) + delete saveComments->at(i); + saveComments->clear(); + delete saveComments; + saveComments = NULL; + } + notifySaveChanged(); + notifySaveCommentsChanged(); + + if(!updateSaveDataWorking) + { + updateSaveDataWorking = true; + updateSaveDataFinished = false; + pthread_create(&updateSaveDataThread, 0, &PreviewModel::updateSaveDataTHelper, this); + } + + if(!updateSaveInfoWorking) + { + updateSaveInfoWorking = true; + updateSaveInfoFinished = false; + pthread_create(&updateSaveInfoThread, 0, &PreviewModel::updateSaveInfoTHelper, this); + } + + if(!updateSaveCommentsWorking) + { + commentsLoaded = false; + updateSaveCommentsWorking = true; + updateSaveCommentsFinished = false; + pthread_create(&updateSaveCommentsThread, 0, &PreviewModel::updateSaveCommentsTHelper, this); + } +} + +void PreviewModel::SetDoOpen(bool doOpen) +{ + this->doOpen = doOpen; +} + +bool PreviewModel::GetDoOpen() +{ + return doOpen; +} + +SaveInfo * PreviewModel::GetSave() +{ + return save; +} + +int PreviewModel::GetCommentsPageNum() +{ + return commentsPageNumber; +} + +int PreviewModel::GetCommentsPageCount() +{ + return max(1, (int)(ceil(commentsTotal/20.0f))); +} + +bool PreviewModel::GetCommentsLoaded() +{ + return commentsLoaded; +} + +void PreviewModel::UpdateComments(int pageNumber) +{ + commentsLoaded = false; + if(saveComments) + { + for(int i = 0; i < saveComments->size(); i++) + delete saveComments->at(i); + saveComments->clear(); + delete saveComments; + saveComments = NULL; + } + + //resultCount = 0; + commentsPageNumber = pageNumber; + notifySaveCommentsChanged(); + notifyCommentsPageChanged(); + + //Threading + if(!updateSaveCommentsWorking) + { + updateSaveCommentsFinished = false; + updateSaveCommentsWorking = true; + pthread_create(&updateSaveCommentsThread, 0, &PreviewModel::updateSaveCommentsTHelper, this); + } +} + +std::vector<SaveComment*> * PreviewModel::GetComments() +{ + return saveComments; +} + +void PreviewModel::notifySaveChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifySaveChanged(this); + } +} + +void PreviewModel::notifyCommentBoxEnabledChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyCommentBoxEnabledChanged(this); + } +} + +void PreviewModel::notifyCommentsPageChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyCommentsPageChanged(this); + } +} + +void PreviewModel::notifySaveCommentsChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyCommentsChanged(this); + } +} + +void PreviewModel::AddObserver(PreviewView * observer) { + observers.push_back(observer); + observer->NotifySaveChanged(this); + observer->NotifyCommentsChanged(this); + observer->NotifyCommentsPageChanged(this); + observer->NotifyCommentBoxEnabledChanged(this); +} + +void PreviewModel::Update() +{ + if(updateSaveDataWorking) + { + if(updateSaveDataFinished) + { + updateSaveDataWorking = false; + pthread_join(updateSaveDataThread, NULL); + + if(updateSaveInfoFinished && save) + { + commentsTotal = save->Comments; + try + { + save->SetGameSave(new GameSave(&saveDataBuffer[0], saveDataBuffer.size())); + } + catch(ParseException &e) + { + throw PreviewModelException("Save file corrupt or from newer version"); + } + notifySaveChanged(); + notifyCommentsPageChanged(); + } + } + } + + if(updateSaveInfoWorking) + { + if(updateSaveInfoFinished) + { + if(save) + { + delete save; + save = NULL; + } + updateSaveInfoWorking = false; + pthread_join(updateSaveInfoThread, (void**)(&save)); + if(updateSaveDataFinished && save) + { + commentsTotal = save->Comments; + try + { + save->SetGameSave(new GameSave(&saveDataBuffer[0], saveDataBuffer.size())); + } + catch(ParseException &e) + { + throw PreviewModelException("Save file corrupt or from newer version"); + } + notifyCommentsPageChanged(); + } + notifySaveChanged(); + + if(!save) + throw PreviewModelException("Unable to load save"); + } + } + + if(updateSaveCommentsWorking) + { + if(updateSaveCommentsFinished) + { + if(saveComments) + { + for(int i = 0; i < saveComments->size(); i++) + delete saveComments->at(i); + saveComments->clear(); + delete saveComments; + saveComments = NULL; + } + commentsLoaded = true; + updateSaveCommentsWorking = false; + pthread_join(updateSaveCommentsThread, (void**)(&saveComments)); + notifySaveCommentsChanged(); + } + } +} + +PreviewModel::~PreviewModel() { + pthread_cancel(updateSaveDataThread); + pthread_cancel(updateSaveInfoThread); + pthread_cancel(updateSaveCommentsThread); + if(save) + delete save; + if(saveComments) + { + for(int i = 0; i < saveComments->size(); i++) + delete saveComments->at(i); + saveComments->clear(); + delete saveComments; + } + saveDataBuffer.clear(); +} + diff --git a/src/gui/preview/PreviewModel.h b/src/gui/preview/PreviewModel.h new file mode 100644 index 0000000..846614c --- /dev/null +++ b/src/gui/preview/PreviewModel.h @@ -0,0 +1,85 @@ +#ifndef PREVIEWMODEL_H_ +#define PREVIEWMODEL_H_ + +#include <vector> +#include <iostream> +#include <pthread.h> +#undef GetUserName //God dammit microsoft! +#include "PreviewView.h" +#include "client/SaveInfo.h" +#include "gui/preview/Comment.h" +#include "gui/search/Thumbnail.h" + +using namespace std; + +struct SaveData +{ + unsigned char * data; + int length; +}; + +class PreviewView; +class PreviewModel { + bool doOpen; + bool commentBoxEnabled; + vector<PreviewView*> observers; + SaveInfo * save; + vector<char> saveDataBuffer; + std::vector<SaveComment*> * saveComments; + void notifySaveChanged(); + void notifySaveCommentsChanged(); + void notifyCommentsPageChanged(); + void notifyCommentBoxEnabledChanged(); + + //Background retrieval + int tSaveID; + int tSaveDate; + + // + bool commentsLoaded; + int commentsTotal; + int commentsPageNumber; + + bool updateSaveDataWorking; + volatile bool updateSaveDataFinished; + pthread_t updateSaveDataThread; + static void * updateSaveDataTHelper(void * obj); + static void updateSaveDataTDelete(void * arg); + void * updateSaveDataT(); + + bool updateSaveInfoWorking; + volatile bool updateSaveInfoFinished; + pthread_t updateSaveInfoThread; + static void * updateSaveInfoTHelper(void * obj); + static void updateSaveInfoTDelete(void * arg); + void * updateSaveInfoT(); + + bool updateSaveCommentsWorking; + volatile bool updateSaveCommentsFinished; + pthread_t updateSaveCommentsThread; + static void * updateSaveCommentsTHelper(void * obj); + static void updateSaveCommentsTDelete(void * arg); + void * updateSaveCommentsT(); +public: + PreviewModel(); + SaveInfo * GetSave(); + std::vector<SaveComment*> * GetComments(); + + bool GetCommentBoxEnabled(); + void SetCommentBoxEnabled(bool enabledState); + + bool GetCommentsLoaded(); + int GetCommentsPageNum(); + int GetCommentsPageCount(); + void UpdateComments(int pageNumber); + + void AddObserver(PreviewView * observer); + void UpdateSave(int saveID, int saveDate); + void SetFavourite(bool favourite); + bool GetDoOpen(); + void SetDoOpen(bool doOpen); + void Update(); + virtual ~PreviewModel(); +}; + +#endif /* PREVIEWMODEL_H_ */ diff --git a/src/gui/preview/PreviewModelException.h b/src/gui/preview/PreviewModelException.h new file mode 100644 index 0000000..85dce1d --- /dev/null +++ b/src/gui/preview/PreviewModelException.h @@ -0,0 +1,19 @@ +#ifndef PREVIEWMODELEXCEPTION_H_ +#define PREVIEWMODELEXCEPTION_H_ + +#include <string> +#include <exception> +using namespace std; + +struct PreviewModelException: public exception { + string message; +public: + PreviewModelException(string message_): message(message_) {} + const char * what() const throw() + { + return message.c_str(); + } + ~PreviewModelException() throw() {}; +}; + +#endif /* PREVIEWMODELEXCEPTION_H_ */ diff --git a/src/gui/preview/PreviewView.cpp b/src/gui/preview/PreviewView.cpp new file mode 100644 index 0000000..4211732 --- /dev/null +++ b/src/gui/preview/PreviewView.cpp @@ -0,0 +1,614 @@ +#include <sstream> +#include <vector> +#include <cmath> +#include "PreviewView.h" +#include "gui/dialogues/TextPrompt.h" +#include "simulation/SaveRenderer.h" +#include "gui/interface/Point.h" +#include "gui/interface/Window.h" +#include "gui/interface/Textbox.h" +#include "gui/Style.h" +#include "Format.h" +#include "gui/search/Thumbnail.h" +#include "client/Client.h" +#include "gui/interface/ScrollPanel.h" +#include "gui/interface/AvatarButton.h" +#include "gui/interface/Keys.h" + +class PreviewView::LoginAction: public ui::ButtonAction +{ + PreviewView * v; +public: + LoginAction(PreviewView * v_){ v = v_; } + virtual void ActionCallback(ui::Button * sender) + { + v->c->ShowLogin(); + } +}; + +class PreviewView::SubmitCommentAction: public ui::ButtonAction +{ + PreviewView * v; +public: + SubmitCommentAction(PreviewView * v_){ v = v_; } + virtual void ActionCallback(ui::Button * sender) + { + v->submitComment(); + } +}; + +class PreviewView::AutoCommentSizeAction: public ui::TextboxAction +{ + PreviewView * v; +public: + AutoCommentSizeAction(PreviewView * v): v(v) {} + virtual void TextChangedCallback(ui::Textbox * sender) { + v->commentBoxAutoHeight(); + } +}; + +PreviewView::PreviewView(): + ui::Window(ui::Point(-1, -1), ui::Point((XRES/2)+210, (YRES/2)+150)), + savePreview(NULL), + doOpen(false), + addCommentBox(NULL), + submitCommentButton(NULL), + commentBoxHeight(20), + showAvatars(true) +{ + class FavAction: public ui::ButtonAction + { + PreviewView * v; + public: + FavAction(PreviewView * v_){ v = v_; } + virtual void ActionCallback(ui::Button * sender) + { + v->c->FavouriteSave(); + } + }; + + showAvatars = Client::Ref().GetPrefBool("ShowAvatars", true); + + favButton = new ui::Button(ui::Point(50, Size.Y-19), ui::Point(51, 19), "Fav"); + favButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; favButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + favButton->SetIcon(IconFavourite); + favButton->SetActionCallback(new FavAction(this)); + favButton->Enabled = Client::Ref().GetAuthUser().ID?true:false; + AddComponent(favButton); + + class ReportPromptCallback: public TextDialogueCallback { + public: + PreviewView * v; + ReportPromptCallback(PreviewView * v_) { v = v_; } + virtual void TextCallback(TextPrompt::DialogueResult result, std::string resultText) { + if (result == TextPrompt::ResultOkay) + v->c->Report(resultText); + } + virtual ~ReportPromptCallback() { } + }; + + class ReportAction: public ui::ButtonAction + { + PreviewView * v; + public: + ReportAction(PreviewView * v_){ v = v_; } + virtual void ActionCallback(ui::Button * sender) + { + new TextPrompt("Report Save", "Things to consider when reporting:\n\bw1)\bg When reporting stolen saves, please include the ID of the original save.\n\bw2)\bg Do not waste staff time with fake or bogus reports, doing so may result in a ban.", "", "[reason]", true, new ReportPromptCallback(v)); + } + }; + reportButton = new ui::Button(ui::Point(100, Size.Y-19), ui::Point(51, 19), "Report"); + reportButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; reportButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + reportButton->SetIcon(IconReport); + reportButton->SetActionCallback(new ReportAction(this)); + reportButton->Enabled = Client::Ref().GetAuthUser().ID?true:false; + AddComponent(reportButton); + + class OpenAction: public ui::ButtonAction + { + PreviewView * v; + public: + OpenAction(PreviewView * v_){ v = v_; } + virtual void ActionCallback(ui::Button * sender) + { + v->c->DoOpen(); + } + }; + openButton = new ui::Button(ui::Point(0, Size.Y-19), ui::Point(51, 19), "Open"); + openButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; openButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + openButton->SetIcon(IconOpen); + openButton->SetActionCallback(new OpenAction(this)); + AddComponent(openButton); + + class BrowserOpenAction: public ui::ButtonAction + { + PreviewView * v; + public: + BrowserOpenAction(PreviewView * v_){ v = v_; } + virtual void ActionCallback(ui::Button * sender) + { + v->c->OpenInBrowser(); + } + }; + + browserOpenButton = new ui::Button(ui::Point((XRES/2)-107, Size.Y-19), ui::Point(108, 19), "Open in browser"); + browserOpenButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; browserOpenButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + browserOpenButton->SetIcon(IconOpen); + browserOpenButton->SetActionCallback(new BrowserOpenAction(this)); + AddComponent(browserOpenButton); + + if(showAvatars) + saveNameLabel = new ui::Label(ui::Point(39, (YRES/2)+4), ui::Point(100, 16), ""); + else + saveNameLabel = new ui::Label(ui::Point(5, (YRES/2)+4), ui::Point(100, 16), ""); + saveNameLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + saveNameLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(saveNameLabel); + + if(showAvatars) + saveDescriptionLabel = new ui::Label(ui::Point(5, (YRES/2)+4+15+21), ui::Point((XRES/2)-10, Size.Y-((YRES/2)+4+15+17)-25), ""); + else + saveDescriptionLabel = new ui::Label(ui::Point(5, (YRES/2)+4+15+19), ui::Point((XRES/2)-10, Size.Y-((YRES/2)+4+15+17)-23), ""); + saveDescriptionLabel->SetMultiline(true); + saveDescriptionLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + saveDescriptionLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop; + saveDescriptionLabel->SetTextColour(ui::Colour(180, 180, 180)); + AddComponent(saveDescriptionLabel); + + if(showAvatars) + authorDateLabel = new ui::Label(ui::Point(39, (YRES/2)+4+15), ui::Point(180, 16), ""); + else + authorDateLabel = new ui::Label(ui::Point(5, (YRES/2)+4+15), ui::Point(200, 16), ""); + authorDateLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + authorDateLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(authorDateLabel); + + if(showAvatars) + { + avatarButton = new ui::AvatarButton(ui::Point(4, (YRES/2)+4), ui::Point(34, 34), ""); + AddComponent(avatarButton); + } + + viewsLabel = new ui::Label(ui::Point((XRES/2)-80, (YRES/2)+4+15), ui::Point(80, 16), ""); + viewsLabel->Appearance.HorizontalAlign = ui::Appearance::AlignRight; + viewsLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(viewsLabel); + + + pageInfo = new ui::Label(ui::Point((XRES/2) + 5, Size.Y+1), ui::Point(Size.X-((XRES/2) + 10), 15), "Page 1 of 1"); + pageInfo->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; authorDateLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + + saveIDTextbox = new ui::Textbox(ui::Point((XRES/2)-55, Size.Y-40), ui::Point(50, 16), "0000000"); + saveIDTextbox->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; + saveIDTextbox->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + saveIDTextbox->ReadOnly = true; + AddComponent(saveIDTextbox); + + class CopyIDAction: public ui::ButtonAction + { + PreviewView * v; + public: + CopyIDAction(PreviewView * v_){ v = v_; } + virtual void ActionCallback(ui::Button * sender) + { + ClipboardPush((char*)v->saveIDTextbox->GetText().c_str()); + } + }; + + ui::Button * tempButton = new ui::Button(ui::Point((XRES/2)-130, Size.Y-40), ui::Point(70, 16), "Copy Save ID"); + tempButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; + tempButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + tempButton->SetActionCallback(new CopyIDAction(this)); + AddComponent(tempButton); + + commentsPanel = new ui::ScrollPanel(ui::Point((XRES/2)+1, 1), ui::Point((Size.X-(XRES/2))-2, Size.Y-commentBoxHeight)); + AddComponent(commentsPanel); + + AddComponent(pageInfo); +} + +void PreviewView::AttachController(PreviewController * controller) +{ + c = controller; + saveIDTextbox->SetText(format::NumberToString<int>(c->SaveID())); +} + +void PreviewView::commentBoxAutoHeight() +{ + if(!addCommentBox) + return; + int textWidth = Graphics::textwidth(addCommentBox->GetText().c_str()); + if(textWidth+15 > Size.X-(XRES/2)-48) + { + addCommentBox->Appearance.VerticalAlign = ui::Appearance::AlignTop; + + int oldSize = addCommentBox->Size.Y; + addCommentBox->AutoHeight(); + int newSize = addCommentBox->Size.Y+5; + addCommentBox->Size.Y = oldSize; + + commentBoxHeight = newSize+22; + commentBoxPositionX = (XRES/2)+4; + commentBoxPositionY = Size.Y-(newSize+21); + commentBoxSizeX = Size.X-(XRES/2)-8; + commentBoxSizeY = newSize; + } + else + { + commentBoxHeight = 20; + addCommentBox->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + + commentBoxPositionX = (XRES/2)+4; + commentBoxPositionY = Size.Y-19; + commentBoxSizeX = Size.X-(XRES/2)-48; + commentBoxSizeY = 17; + } + commentsPanel->Size.Y = Size.Y-commentBoxHeight; +} + +void PreviewView::DoDraw() +{ + Window::DoDraw(); + Graphics * g = ui::Engine::Ref().g; + for(int i = 0; i < commentTextComponents.size(); i++) + { + int linePos = commentTextComponents[i]->Position.Y+commentsPanel->ViewportPosition.Y+commentTextComponents[i]->Size.Y+4; + if(linePos > 0 && linePos < Size.Y-commentBoxHeight) + g->draw_line( + Position.X+1+XRES/2, + Position.Y+linePos, + Position.X+Size.X-2, + Position.Y+linePos, + 255, 255, 255, 100); + } + if(c->GetDoOpen()) + { + g->fillrect(Position.X+(Size.X/2)-101, Position.Y+(Size.Y/2)-26, 202, 52, 0, 0, 0, 210); + g->drawrect(Position.X+(Size.X/2)-100, Position.Y+(Size.Y/2)-25, 200, 50, 255, 255, 255, 180); + g->drawtext(Position.X+(Size.X/2)-(Graphics::textwidth("Loading save...")/2), Position.Y+(Size.Y/2)-5, "Loading save...", style::Colour::InformationTitle.Red, style::Colour::InformationTitle.Green, style::Colour::InformationTitle.Blue, 255); + } + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + +} + +void PreviewView::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + + //Window Background+Outline + g->clearrect(Position.X-2, Position.Y-2, Size.X+4, Size.Y+4); + + //Save preview (top-left) + if(savePreview && savePreview->Buffer) + { + g->draw_image(savePreview, (Position.X+1)+(((XRES/2)-savePreview->Width)/2), (Position.Y+1)+(((YRES/2)-savePreview->Height)/2), 255); + } + g->drawrect(Position.X, Position.Y, (XRES/2)+1, (YRES/2)+1, 255, 255, 255, 100); + g->draw_line(Position.X+XRES/2, Position.Y+1, Position.X+XRES/2, Position.Y+Size.Y-2, 200, 200, 200, 255); + + if(votesUp || votesDown) + { + float ryf; + int nyu, nyd; + int lv = (votesUp>votesDown)?votesUp:votesDown; + lv = (lv>10)?lv:10; + + if (50>lv) + { + ryf = 50.0f/((float)lv); + nyu = votesUp*ryf; + nyd = votesDown*ryf; + } + else + { + ryf = ((float)lv)/50.0f; + nyu = votesUp/ryf; + nyd = votesDown/ryf; + } + nyu = nyu>50?50:nyu; + nyd = nyd>50?50:nyd; + + g->fillrect(Position.X+(XRES/2)-55, Position.Y+(YRES/2)+3, 53, 7, 0, 107, 10, 255); + g->fillrect(Position.X+(XRES/2)-55, Position.Y+(YRES/2)+9, 53, 7, 107, 10, 0, 255); + g->drawrect(Position.X+(XRES/2)-55, Position.Y+(YRES/2)+3, 53, 7, 128, 128, 128, 255); + g->drawrect(Position.X+(XRES/2)-55, Position.Y+(YRES/2)+9, 53, 7, 128, 128, 128, 255); + + g->fillrect(Position.X+(XRES/2)-4-nyu, Position.Y+(YRES/2)+5, nyu, 3, 57, 187, 57, 255); + g->fillrect(Position.X+(XRES/2)-4-nyd, Position.Y+(YRES/2)+11, nyd, 3, 187, 57, 57, 255); + } +} + +void PreviewView::OnTick(float dt) +{ + if(addCommentBox) + { + ui::Point positionDiff = ui::Point(commentBoxPositionX, commentBoxPositionY)-addCommentBox->Position; + ui::Point sizeDiff = ui::Point(commentBoxSizeX, commentBoxSizeY)-addCommentBox->Size; + + if(positionDiff.X!=0) + { + int xdiff = positionDiff.X/5; + if(xdiff == 0) + xdiff = 1*isign(positionDiff.X); + addCommentBox->Position.X += xdiff; + } + if(positionDiff.Y!=0) + { + int ydiff = positionDiff.Y/5; + if(ydiff == 0) + ydiff = 1*isign(positionDiff.Y); + addCommentBox->Position.Y += ydiff; + } + + if(sizeDiff.X!=0) + { + int xdiff = sizeDiff.X/5; + if(xdiff == 0) + xdiff = 1*isign(sizeDiff.X); + addCommentBox->Size.X += xdiff; + addCommentBox->Invalidate(); + } + if(sizeDiff.Y!=0) + { + int ydiff = sizeDiff.Y/5; + if(ydiff == 0) + ydiff = 1*isign(sizeDiff.Y); + addCommentBox->Size.Y += ydiff; + addCommentBox->Invalidate(); + } + } + + c->Update(); +} + +void PreviewView::OnTryExit(ExitMethod method) +{ + c->Exit(); +} + +void PreviewView::OnMouseWheel(int x, int y, int d) +{ + if(commentsPanel->GetScrollLimit() == 1 && d < 0) + c->NextCommentPage(); + if(commentsPanel->GetScrollLimit() == -1 && d > 0) + c->PrevCommentPage(); + +} + +void PreviewView::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + if ((key == KEY_ENTER || key == KEY_RETURN) && (!addCommentBox || !addCommentBox->IsFocused())) + openButton->DoAction(); +} + +void PreviewView::NotifySaveChanged(PreviewModel * sender) +{ + SaveInfo * save = sender->GetSave(); + if(savePreview) + delete savePreview; + savePreview = NULL; + if(save) + { + votesUp = save->votesUp; + votesDown = save->votesDown; + saveNameLabel->SetText(save->name); + if(showAvatars) { + avatarButton->SetUsername(save->userName); + authorDateLabel->SetText("\bw" + save->userName + " \bgDate:\bw " + format::UnixtimeToDateMini(save->date)); + } + else + { + authorDateLabel->SetText("\bgAuthor:\bw " + save->userName + " \bgDate:\bw " + format::UnixtimeToDateMini(save->date)); + } + viewsLabel->SetText("\bgViews:\bw " + format::NumberToString<int>(save->Views)); + saveDescriptionLabel->SetText(save->Description); + if(save->Favourite) + { + favButton->Enabled = true; + favButton->SetText("Unfav"); + } + else if(Client::Ref().GetAuthUser().ID) + { + favButton->Enabled = true; + favButton->SetText("Fav"); + } + else + { + favButton->SetText("Fav"); + favButton->Enabled = false; + } + + if(save->GetGameSave()) + { + savePreview = SaveRenderer::Ref().Render(save->GetGameSave(), false, true); + + if(savePreview && savePreview->Buffer && !(savePreview->Width == XRES/2 && savePreview->Width == YRES/2)) + { + int newSizeX, newSizeY; + pixel * oldData = savePreview->Buffer; + float factorX = ((float)XRES/2)/((float)savePreview->Width); + float factorY = ((float)YRES/2)/((float)savePreview->Height); + float scaleFactor = factorY < factorX ? factorY : factorX; + savePreview->Buffer = Graphics::resample_img(oldData, savePreview->Width, savePreview->Height, savePreview->Width*scaleFactor, savePreview->Height*scaleFactor); + delete[] oldData; + savePreview->Width *= scaleFactor; + savePreview->Height *= scaleFactor; + } + } + } + else + { + votesUp = 0; + votesDown = 0; + saveNameLabel->SetText(""); + authorDateLabel->SetText(""); + saveDescriptionLabel->SetText(""); + favButton->Enabled = false; + } +} + +void PreviewView::submitComment() +{ + if(addCommentBox) + { + std::string comment = std::string(addCommentBox->GetText()); + submitCommentButton->Enabled = false; + addCommentBox->SetText(""); + addCommentBox->SetPlaceholder("Submitting comment"); //This doesn't appear to ever show since no separate thread is created + FocusComponent(NULL); + + if (!c->SubmitComment(comment)) + addCommentBox->SetText(comment); + + addCommentBox->SetPlaceholder("Add comment"); + submitCommentButton->Enabled = true; + + commentBoxAutoHeight(); + } +} + +void PreviewView::NotifyCommentBoxEnabledChanged(PreviewModel * sender) +{ + if(addCommentBox) + { + RemoveComponent(addCommentBox); + delete addCommentBox; + addCommentBox = NULL; + } + if(submitCommentButton) + { + RemoveComponent(submitCommentButton); + delete submitCommentButton; + submitCommentButton = NULL; + } + if(sender->GetCommentBoxEnabled()) + { + commentBoxPositionX = (XRES/2)+4; + commentBoxPositionY = Size.Y-19; + commentBoxSizeX = Size.X-(XRES/2)-48; + commentBoxSizeY = 17; + + addCommentBox = new ui::Textbox(ui::Point((XRES/2)+4, Size.Y-19), ui::Point(Size.X-(XRES/2)-48, 17), "", "Add Comment"); + addCommentBox->SetActionCallback(new AutoCommentSizeAction(this)); + addCommentBox->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + addCommentBox->SetMultiline(true); + AddComponent(addCommentBox); + submitCommentButton = new ui::Button(ui::Point(Size.X-40, Size.Y-19), ui::Point(40, 19), "Submit"); + submitCommentButton->SetActionCallback(new SubmitCommentAction(this)); + //submitCommentButton->Enabled = false; + AddComponent(submitCommentButton); + } + else + { + submitCommentButton = new ui::Button(ui::Point(XRES/2, Size.Y-19), ui::Point(Size.X-(XRES/2), 19), "Login to comment"); + submitCommentButton->SetActionCallback(new LoginAction(this)); + AddComponent(submitCommentButton); + } +} + +void PreviewView::NotifyCommentsPageChanged(PreviewModel * sender) +{ + std::stringstream pageInfoStream; + pageInfoStream << "Page " << sender->GetCommentsPageNum() << " of " << sender->GetCommentsPageCount(); + pageInfo->SetText(pageInfoStream.str()); +} + +void PreviewView::NotifyCommentsChanged(PreviewModel * sender) +{ + std::vector<SaveComment*> * comments = sender->GetComments(); + + for(int i = 0; i < commentComponents.size(); i++) + { + commentsPanel->RemoveChild(commentComponents[i]); + delete commentComponents[i]; + } + commentComponents.clear(); + commentTextComponents.clear(); + commentsPanel->InnerSize = ui::Point(0, 0); + + if(comments) + { + for(int i = 0; i < commentComponents.size(); i++) + { + commentsPanel->RemoveChild(commentComponents[i]); + delete commentComponents[i]; + } + commentComponents.clear(); + commentTextComponents.clear(); + + int currentY = 0;//-yOffset; + ui::Label * tempUsername; + ui::Label * tempComment; + ui::AvatarButton * tempAvatar; + for(int i = 0; i < comments->size(); i++) + { + int usernameY = currentY+5, commentY; + if(showAvatars) + { + tempAvatar = new ui::AvatarButton(ui::Point(2, currentY+7), ui::Point(26, 26), comments->at(i)->authorName); + commentComponents.push_back(tempAvatar); + commentsPanel->AddChild(tempAvatar); + } + + if(showAvatars) + tempUsername = new ui::Label(ui::Point(31, currentY+3), ui::Point(Size.X-((XRES/2) + 13), 16), comments->at(i)->authorNameFormatted); + else + tempUsername = new ui::Label(ui::Point(5, currentY+3), ui::Point(Size.X-((XRES/2) + 13), 16), comments->at(i)->authorNameFormatted); + tempUsername->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + tempUsername->Appearance.VerticalAlign = ui::Appearance::AlignBottom; + currentY += 16; + + commentComponents.push_back(tempUsername); + commentsPanel->AddChild(tempUsername); + + commentY = currentY+5; + if(showAvatars) + tempComment = new ui::Label(ui::Point(31, currentY+5), ui::Point(Size.X-((XRES/2) + 13 + 26), -1), comments->at(i)->comment); + else + tempComment = new ui::Label(ui::Point(5, currentY+5), ui::Point(Size.X-((XRES/2) + 13), -1), comments->at(i)->comment); + tempComment->SetMultiline(true); + tempComment->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + tempComment->Appearance.VerticalAlign = ui::Appearance::AlignTop; + tempComment->SetTextColour(ui::Colour(180, 180, 180)); + currentY += tempComment->Size.Y+4; + + commentComponents.push_back(tempComment); + commentsPanel->AddChild(tempComment); + commentTextComponents.push_back(tempComment); + } + + commentsPanel->InnerSize = ui::Point(commentsPanel->Size.X, currentY+4); + } +} + +/*void PreviewView::NotifyPreviewChanged(PreviewModel * sender) +{ + savePreview = sender->GetGameSave(); + if(savePreview && savePreview->Data && !(savePreview->Width == XRES/2 && savePreview->Height == YRES/2)) + { + int newSizeX, newSizeY; + float factorX = ((float)XRES/2)/((float)savePreview->Width); + float factorY = ((float)YRES/2)/((float)savePreview->Height); + float scaleFactor = factorY < factorX ? factorY : factorX; + savePreview->Data = Graphics::resample_img(savePreview->Data, savePreview->Width, savePreview->Height, savePreview->Width*scaleFactor, savePreview->Height*scaleFactor); + savePreview->Width *= scaleFactor; + savePreview->Height *= scaleFactor; + } +}*/ + +PreviewView::~PreviewView() +{ + if(addCommentBox) + { + RemoveComponent(addCommentBox); + delete addCommentBox; + } + if(submitCommentButton) + { + RemoveComponent(submitCommentButton); + delete submitCommentButton; + } + if(savePreview) + delete savePreview; +} + diff --git a/src/gui/preview/PreviewView.h b/src/gui/preview/PreviewView.h new file mode 100644 index 0000000..2fef5ac --- /dev/null +++ b/src/gui/preview/PreviewView.h @@ -0,0 +1,74 @@ +#ifndef PREVIEWVIEW_H_ +#define PREVIEWVIEW_H_ + +#include <vector> +#include "Comment.h" +#include "gui/interface/Window.h" +#include "gui/preview/PreviewController.h" +#include "gui/preview/PreviewModel.h" +#include "gui/interface/Button.h" +#include "gui/interface/Label.h" +#include "gui/interface/Textbox.h" + +namespace ui +{ + class ScrollPanel; + class AvatarButton; +} + +class VideoBuffer; +class PreviewModel; +class PreviewController; +class PreviewView: public ui::Window { + class SubmitCommentAction; + class LoginAction; + class AutoCommentSizeAction; + PreviewController * c; + VideoBuffer * savePreview; + ui::Button * openButton; + ui::Button * browserOpenButton; + ui::Button * favButton; + ui::Button * reportButton; + ui::Button * submitCommentButton; + ui::Textbox * addCommentBox; + ui::Label * saveNameLabel; + ui::Label * authorDateLabel; + ui::AvatarButton * avatarButton; + ui::Label * pageInfo; + ui::Label * saveDescriptionLabel; + ui::Label * viewsLabel; + ui::Textbox * saveIDTextbox; + ui::ScrollPanel * commentsPanel; + std::vector<ui::Component*> commentComponents; + std::vector<ui::Component*> commentTextComponents; + int votesUp; + int votesDown; + bool doOpen; + bool showAvatars; + + int commentBoxHeight; + float commentBoxPositionX; + float commentBoxPositionY; + float commentBoxSizeX; + float commentBoxSizeY; + + void displayComments(); + void commentBoxAutoHeight(); + void submitComment(); +public: + void AttachController(PreviewController * controller); + PreviewView(); + void NotifySaveChanged(PreviewModel * sender); + void NotifyCommentsChanged(PreviewModel * sender); + void NotifyCommentsPageChanged(PreviewModel * sender); + void NotifyCommentBoxEnabledChanged(PreviewModel * sender); + virtual void OnDraw(); + virtual void DoDraw(); + virtual void OnTick(float dt); + virtual void OnTryExit(ExitMethod method); + virtual void OnMouseWheel(int x, int y, int d); + virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + virtual ~PreviewView(); +}; + +#endif /* PREVIEWVIEW_H_ */ diff --git a/src/gui/profile/ProfileActivity.cpp b/src/gui/profile/ProfileActivity.cpp new file mode 100644 index 0000000..54da81e --- /dev/null +++ b/src/gui/profile/ProfileActivity.cpp @@ -0,0 +1,174 @@ +#include <algorithm> +#include "ProfileActivity.h" +#include "gui/interface/Button.h" +#include "gui/interface/Textbox.h" +#include "gui/interface/Label.h" +#include "gui/interface/AvatarButton.h" +#include "gui/interface/ScrollPanel.h" +#include "gui/interface/Keys.h" +#include "gui/Style.h" +#include "client/Client.h" +#include "client/UserInfo.h" +#include "client/requestbroker/RequestListener.h" + +ProfileActivity::ProfileActivity(std::string username) : + WindowActivity(ui::Point(-1, -1), ui::Point(236, 200)), + loading(false), + saving(false) +{ + editable = Client::Ref().GetAuthUser().ID && Client::Ref().GetAuthUser().Username == username; + + + class CloseAction: public ui::ButtonAction + { + ProfileActivity * a; + public: + CloseAction(ProfileActivity * a) : a(a) { } + void ActionCallback(ui::Button * sender_) + { + a->Exit(); + } + }; + + class SaveAction: public ui::ButtonAction + { + ProfileActivity * a; + public: + SaveAction(ProfileActivity * a) : a(a) { } + void ActionCallback(ui::Button * sender_) + { + if(!a->loading && !a->saving && a->editable) + { + sender_->Enabled = false; + sender_->SetText("Saving..."); + a->saving = true; + a->info.Location = ((ui::Textbox*)a->location)->GetText(); + a->info.Biography = ((ui::Textbox*)a->bio)->GetText(); + RequestBroker::Ref().Start(Client::Ref().SaveUserInfoAsync(a->info), a); + } + } + }; + + ui::Button * closeButton = new ui::Button(ui::Point(0, Size.Y-15), ui::Point((Size.X/2)+1, 15), "Close"); + closeButton->SetActionCallback(new CloseAction(this)); + + if(editable) + { + ui::Button * saveButton = new ui::Button(ui::Point(Size.X/2, Size.Y-15), ui::Point(Size.X/2, 15), "Save"); + saveButton->SetActionCallback(new SaveAction(this)); + AddComponent(saveButton); + } + + AddComponent(closeButton); + + loading = true; + RequestBroker::Ref().Start(Client::Ref().GetUserInfoAsync(username), this); +} + +void ProfileActivity::setUserInfo(UserInfo newInfo) +{ + info = newInfo; + + if(!info.Biography.length() && !editable) + info.Biography = "\bg(no bio)"; + + if(!info.Location.length() && !editable) + info.Location = "\bg(no location)"; + + ui::AvatarButton * avatar = new ui::AvatarButton(ui::Point((Size.X-40)-8, 8), ui::Point(40, 40), info.Username); + AddComponent(avatar); + + int currentY = 5; + ui::Label * title = new ui::Label(ui::Point(4, currentY), ui::Point(Size.X-8-(40+8), 15), info.Username); + title->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + AddComponent(title); + currentY += 20; + + ui::Label * locationTitle = new ui::Label(ui::Point(4, currentY), ui::Point(Size.X-8-(40+8), 15), "Location"); + locationTitle->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + AddComponent(locationTitle); + currentY += 17; + + if(editable) + { + ui::Textbox * location = new ui::Textbox(ui::Point(8, currentY), ui::Point(Size.X-16-(40+8), 17), info.Location); + location->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + AddComponent(location); + this->location = location; + currentY += 10+location->Size.Y; + } + else + { + ui::Label * location = new ui::Label(ui::Point(4, currentY), ui::Point(Size.X-8-(40+8), 12), info.Location); + location->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + location->SetTextColour(ui::Colour(180, 180, 180)); + AddComponent(location); + this->location = location; + currentY += 10+location->Size.Y; + } + + ui::Label * bioTitle = new ui::Label(ui::Point(4, currentY), ui::Point(Size.X-8, 15), "Biography"); + bioTitle->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + AddComponent(bioTitle); + currentY += 17; + + if(editable) + { + ui::Textbox * bio = new ui::Textbox(ui::Point(8, currentY), ui::Point(Size.X-16, Size.Y-30-currentY), info.Biography); + bio->SetMultiline(true); + bio->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + bio->Appearance.VerticalAlign = ui::Appearance::AlignTop; + AddComponent(bio); + currentY += 10+bio->Size.Y; + this->bio = bio; + } + else + { + ui::Label * bio = new ui::Label(ui::Point(4, currentY), ui::Point(Size.X-8, -1), info.Biography); + bio->SetMultiline(true); + bio->SetTextColour(ui::Colour(180, 180, 180)); + bio->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + currentY += 10+bio->Size.Y; + if(currentY > Size.Y - 20) + { + ui::ScrollPanel * scrollPanel = new ui::ScrollPanel(bio->Position, ui::Point(Size.X, Size.Y-30-bio->Position.Y)); + AddComponent(scrollPanel); + bio->Position = ui::Point(4, 4); + scrollPanel->AddChild(bio); + scrollPanel->InnerSize = ui::Point(Size.X, bio->Size.Y+8); + } + else + { + AddComponent(bio); + } + this->bio = bio; + } + + //exit(0); +} + +void ProfileActivity::OnResponseReady(void * userDataPtr) +{ + if(loading) + { + loading = false; + setUserInfo(*(UserInfo*)userDataPtr); + delete (UserInfo*)userDataPtr; + } + else if(saving) + { + Exit(); + } +} + +void ProfileActivity::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); +} + +ProfileActivity::~ProfileActivity() { + RequestBroker::Ref().DetachRequestListener(this); +} + diff --git a/src/gui/profile/ProfileActivity.h b/src/gui/profile/ProfileActivity.h new file mode 100644 index 0000000..ed10e65 --- /dev/null +++ b/src/gui/profile/ProfileActivity.h @@ -0,0 +1,25 @@ +#ifndef PROFILEACTIVITY_H_ +#define PROFILEACTIVITY_H_ + +#include <string> +#include "Activity.h" +#include "client/requestbroker/RequestListener.h" +#include "client/UserInfo.h" +#include "gui/interface/Window.h" + +class ProfileActivity: public WindowActivity, public RequestListener { + ui::Component * location; + ui::Component * bio; + UserInfo info; + bool editable; + bool loading; + bool saving; + void setUserInfo(UserInfo newInfo); +public: + ProfileActivity(std::string username); + virtual ~ProfileActivity(); + virtual void OnResponseReady(void * userDataPtr); + virtual void OnDraw(); +}; + +#endif /* PROFILEACTIVITY_H_ */ diff --git a/src/gui/render/RenderController.cpp b/src/gui/render/RenderController.cpp new file mode 100644 index 0000000..7b60e97 --- /dev/null +++ b/src/gui/render/RenderController.cpp @@ -0,0 +1,67 @@ +#include "RenderController.h" + +RenderController::RenderController(Renderer * ren, ControllerCallback * callback): + HasExited(false) +{ + renderView = new RenderView(); + renderModel = new RenderModel(); + + renderView->AttachController(this); + renderModel->AddObserver(renderView); + + renderModel->SetRenderer(ren); + this->callback = callback; +} + +void RenderController::SetRenderMode(unsigned int renderMode) +{ + renderModel->SetRenderMode(renderMode); +} + +void RenderController::UnsetRenderMode(unsigned int renderMode) +{ + renderModel->UnsetRenderMode(renderMode); +} + +void RenderController::SetDisplayMode(unsigned int renderMode) +{ + renderModel->SetDisplayMode(renderMode); +} + +void RenderController::UnsetDisplayMode(unsigned int renderMode) +{ + renderModel->UnsetDisplayMode(renderMode); +} + +void RenderController::SetColourMode(unsigned int renderMode) +{ + renderModel->SetColourMode(renderMode); +} + +void RenderController::LoadRenderPreset(int presetNum) +{ + renderModel->LoadRenderPreset(presetNum); +} + +void RenderController::Exit() +{ + if(ui::Engine::Ref().GetWindow() == renderView) + { + ui::Engine::Ref().CloseWindow(); + } + if(callback) + callback->ControllerExit(); + HasExited = true; +} + +RenderController::~RenderController() { + if(ui::Engine::Ref().GetWindow() == renderView) + { + ui::Engine::Ref().CloseWindow(); + } + if(callback) + delete callback; + delete renderModel; + delete renderView; +} + diff --git a/src/gui/render/RenderController.h b/src/gui/render/RenderController.h new file mode 100644 index 0000000..42adb65 --- /dev/null +++ b/src/gui/render/RenderController.h @@ -0,0 +1,29 @@ +#ifndef RENDERCONTROLLER_H_ +#define RENDERCONTROLLER_H_ + +#include "RenderView.h" +#include "RenderModel.h" +#include "graphics/Renderer.h" +#include "Controller.h" + +class RenderView; +class RenderModel; +class RenderController { + RenderView * renderView; + RenderModel * renderModel; + ControllerCallback * callback; +public: + bool HasExited; + RenderController(Renderer * ren, ControllerCallback * callback = NULL); + void Exit(); + RenderView * GetView() { return renderView; } + virtual ~RenderController(); + void SetRenderMode(unsigned int renderMode); + void UnsetRenderMode(unsigned int renderMode); + void SetDisplayMode(unsigned int renderMode); + void UnsetDisplayMode(unsigned int renderMode); + void SetColourMode(unsigned int renderMode); + void LoadRenderPreset(int presetNum); +}; + +#endif /* RENDERCONTROLLER_H_ */ diff --git a/src/gui/render/RenderModel.cpp b/src/gui/render/RenderModel.cpp new file mode 100644 index 0000000..b17f6fc --- /dev/null +++ b/src/gui/render/RenderModel.cpp @@ -0,0 +1,135 @@ +#include "RenderModel.h" + +RenderModel::RenderModel(): + renderer(NULL) +{ + +} + +void RenderModel::AddObserver(RenderView * observer) +{ + observers.push_back(observer); + observer->NotifyRendererChanged(this); + observer->NotifyRenderChanged(this); + observer->NotifyDisplayChanged(this); + observer->NotifyColourChanged(this); +} + +void RenderModel::SetRenderMode(unsigned int renderMode) +{ + if(renderer) + renderer->AddRenderMode(renderMode); + notifyRenderChanged(); +} + +void RenderModel::UnsetRenderMode(unsigned int renderMode) +{ + if(renderer) + renderer->RemoveRenderMode(renderMode); + notifyRenderChanged(); +} + +unsigned int RenderModel::GetRenderMode() +{ + if(renderer) + return renderer->render_mode; + else + return 0; +} + +void RenderModel::SetDisplayMode(unsigned int displayMode) +{ + if(renderer) + renderer->AddDisplayMode(displayMode); + notifyDisplayChanged(); +} + +void RenderModel::UnsetDisplayMode(unsigned int displayMode) +{ + if(renderer) + renderer->RemoveDisplayMode(displayMode); + notifyDisplayChanged(); +} + +unsigned int RenderModel::GetDisplayMode() +{ + if(renderer) + return renderer->display_mode; + else + return 0; +} + +void RenderModel::SetColourMode(unsigned int colourMode) +{ + if(renderer) + renderer->SetColourMode(colourMode); + notifyColourChanged(); +} + +unsigned int RenderModel::GetColourMode() +{ + if(renderer) + return renderer->colour_mode; + else + return 0; +} + +void RenderModel::LoadRenderPreset(int presetNum) +{ + RenderPreset preset = renderer->renderModePresets[presetNum]; + renderer->SetRenderMode(preset.RenderModes); + renderer->SetDisplayMode(preset.DisplayModes); + renderer->SetColourMode(preset.ColourMode); + notifyRenderChanged(); + notifyDisplayChanged(); + notifyColourChanged(); +} + +void RenderModel::SetRenderer(Renderer * ren) +{ + renderer = ren; + notifyRendererChanged(); + notifyRenderChanged(); + notifyDisplayChanged(); + notifyColourChanged(); +} + +Renderer * RenderModel::GetRenderer() +{ + return renderer; +} + +void RenderModel::notifyRendererChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyRendererChanged(this); + } +} + +void RenderModel::notifyRenderChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyRenderChanged(this); + } +} + +void RenderModel::notifyDisplayChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyDisplayChanged(this); + } +} + +void RenderModel::notifyColourChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyColourChanged(this); + } +} + +RenderModel::~RenderModel() { +} diff --git a/src/gui/render/RenderModel.h b/src/gui/render/RenderModel.h new file mode 100644 index 0000000..2cd24a5 --- /dev/null +++ b/src/gui/render/RenderModel.h @@ -0,0 +1,35 @@ +#ifndef RENDERMODEL_H_ +#define RENDERMODEL_H_ + +#include <vector> +#include "RenderView.h" +#include "graphics/Renderer.h" + +using namespace std; + +class RenderView; +class RenderModel { + vector<RenderView*> observers; + Renderer * renderer; + void notifyRendererChanged(); + void notifyRenderChanged(); + void notifyDisplayChanged(); + void notifyColourChanged(); +public: + RenderModel(); + Renderer * GetRenderer(); + void AddObserver(RenderView * observer); + void SetRenderer(Renderer * ren); + void SetRenderMode(unsigned int renderMode); + void UnsetRenderMode(unsigned int renderMode); + unsigned int GetRenderMode(); + void SetDisplayMode(unsigned int displayMode); + void UnsetDisplayMode(unsigned int displayMode); + unsigned int GetDisplayMode(); + void SetColourMode(unsigned int colourMode); + unsigned int GetColourMode(); + void LoadRenderPreset(int presetNum); + virtual ~RenderModel(); +}; + +#endif /* RENDERMODEL_H_ */ diff --git a/src/gui/render/RenderView.cpp b/src/gui/render/RenderView.cpp new file mode 100644 index 0000000..508ff47 --- /dev/null +++ b/src/gui/render/RenderView.cpp @@ -0,0 +1,401 @@ +#include "simulation/ElementGraphics.h" +#include "graphics/Graphics.h" +#include "graphics/Renderer.h" +#include "RenderView.h" + +class RenderView::RenderModeAction: public ui::CheckboxAction +{ + RenderView * v; +public: + unsigned int renderMode; + RenderModeAction(RenderView * v_, unsigned int renderMode_) + { + v = v_; + renderMode = renderMode_; + } + virtual void ActionCallback(ui::Checkbox * sender) + { + if(sender->GetChecked()) + v->c->SetRenderMode(renderMode); + else + v->c->UnsetRenderMode(renderMode); + } +}; + +class RenderView::DisplayModeAction: public ui::CheckboxAction +{ + RenderView * v; +public: + unsigned int displayMode; + DisplayModeAction(RenderView * v_, unsigned int displayMode_) + { + v = v_; + displayMode = displayMode_; + } + virtual void ActionCallback(ui::Checkbox * sender) + { + if(sender->GetChecked()) + v->c->SetDisplayMode(displayMode); + else + v->c->UnsetDisplayMode(displayMode); + } +}; + +class RenderView::ColourModeAction: public ui::CheckboxAction +{ + RenderView * v; +public: + unsigned int colourMode; + ColourModeAction(RenderView * v_, unsigned int colourMode_) + { + v = v_; + colourMode = colourMode_; + } + virtual void ActionCallback(ui::Checkbox * sender) + { + if(sender->GetChecked()) + v->c->SetColourMode(colourMode); + else + v->c->SetColourMode(0); + } +}; + +class RenderView::RenderPresetAction: public ui::ButtonAction +{ + RenderView * v; +public: + int renderPreset; + RenderPresetAction(RenderView * v_, int renderPreset_) + { + v = v_; + renderPreset = renderPreset_; + } + virtual void ActionCallback(ui::Button * sender) + { + v->c->LoadRenderPreset(renderPreset); + } +}; + +RenderView::RenderView(): + ui::Window(ui::Point(0, 0), ui::Point(XRES, YRES+MENUSIZE)), + toolTip(""), + toolTipPresence(0), + ren(NULL) +{ + ui::Button * presetButton; + int presetButtonOffset = 375; + int checkboxOffset = 1; + int cSpace = 32; + int sSpace = 38; + + presetButton = new ui::Button(ui::Point(presetButtonOffset+200, YRES+6), ui::Point(30, 13), "", "Velocity display mode preset"); + presetButton->SetIcon(IconVelocity); + presetButton->SetActionCallback(new RenderPresetAction(this, 1)); + AddComponent(presetButton); + + presetButton = new ui::Button(ui::Point(presetButtonOffset+200, YRES+6+18), ui::Point(30, 13), "", "Pressure display mode preset"); + presetButton->SetIcon(IconPressure); + presetButton->SetActionCallback(new RenderPresetAction(this, 2)); + AddComponent(presetButton); + + presetButton = new ui::Button(ui::Point(presetButtonOffset+161, YRES+6), ui::Point(30, 13), "", "Persistent display mode preset"); + presetButton->SetIcon(IconPersistant); + presetButton->SetActionCallback(new RenderPresetAction(this, 3)); + AddComponent(presetButton); + + presetButton = new ui::Button(ui::Point(presetButtonOffset+161, YRES+6+18), ui::Point(30, 13), "", "Fire display mode preset"); + presetButton->SetIcon(IconFire); + presetButton->SetActionCallback(new RenderPresetAction(this, 4)); + AddComponent(presetButton); + + presetButton = new ui::Button(ui::Point(presetButtonOffset+122, YRES+6), ui::Point(30, 13), "", "Blob display mode preset"); + presetButton->SetIcon(IconBlob); + presetButton->SetActionCallback(new RenderPresetAction(this, 5)); + AddComponent(presetButton); + + presetButton = new ui::Button(ui::Point(presetButtonOffset+122, YRES+6+18), ui::Point(30, 13), "", "Heat display mode preset"); + presetButton->SetIcon(IconHeat); + presetButton->SetActionCallback(new RenderPresetAction(this, 6)); + AddComponent(presetButton); + + presetButton = new ui::Button(ui::Point(presetButtonOffset+83, YRES+6), ui::Point(30, 13), "", "Fancy display mode preset"); + presetButton->SetIcon(IconBlur); + presetButton->SetActionCallback(new RenderPresetAction(this, 7)); + AddComponent(presetButton); + + presetButton = new ui::Button(ui::Point(presetButtonOffset+83, YRES+6+18), ui::Point(30, 13), "", "Nothing display mode preset"); + presetButton->SetIcon(IconBasic); + presetButton->SetActionCallback(new RenderPresetAction(this, 8)); + AddComponent(presetButton); + + presetButton = new ui::Button(ui::Point(presetButtonOffset+44, YRES+6), ui::Point(30, 13), "", "Heat gradient display mode preset"); + presetButton->SetIcon(IconGradient); + presetButton->SetActionCallback(new RenderPresetAction(this, 9)); + AddComponent(presetButton); + + presetButton = new ui::Button(ui::Point(presetButtonOffset+44, YRES+6+18), ui::Point(30, 13), "", "Alternative Velocity display mode preset"); + presetButton->SetIcon(IconAltAir); + presetButton->SetActionCallback(new RenderPresetAction(this, 0)); + AddComponent(presetButton); + + presetButton = new ui::Button(ui::Point(presetButtonOffset+5, YRES+6), ui::Point(30, 13), "", "Life display mode preset"); + presetButton->SetIcon(IconLife); + presetButton->SetActionCallback(new RenderPresetAction(this, 10)); + AddComponent(presetButton); + + ui::Checkbox * tCheckbox; + + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Effects", "Adds Special flare effects to some elements"); + renderModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconEffect); + tCheckbox->SetActionCallback(new RenderModeAction(this, RENDER_EFFE)); + AddComponent(tCheckbox); + + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4+18), ui::Point(30, 16), "Fire", "Fire effect for gasses"); + renderModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconFire); + tCheckbox->SetActionCallback(new RenderModeAction(this, RENDER_FIRE)); + AddComponent(tCheckbox); + + checkboxOffset += cSpace; + + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Glow", "Glow effect on some elements"); + renderModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconGlow); + tCheckbox->SetActionCallback(new RenderModeAction(this, RENDER_GLOW)); + AddComponent(tCheckbox); + + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4+18), ui::Point(30, 16), "Blur", "Blur effect for liquids"); + renderModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconBlur); + tCheckbox->SetActionCallback(new RenderModeAction(this, RENDER_BLUR)); + AddComponent(tCheckbox); + + checkboxOffset += cSpace; + + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Blob", "Makes everything be drawn like a blob"); + renderModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconBlob); + tCheckbox->SetActionCallback(new RenderModeAction(this, RENDER_BLOB)); + AddComponent(tCheckbox); + + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4+18), ui::Point(30, 16), "Point", "Basic rendering, without this, most things will be invisible"); + renderModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconBasic); + tCheckbox->SetActionCallback(new RenderModeAction(this, RENDER_BASC)); + AddComponent(tCheckbox); + + checkboxOffset += sSpace; + line1 = checkboxOffset-5; + + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Alt. Air", "Displays pressure as red and blue, and velocity as white"); + displayModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconAltAir); + tCheckbox->SetActionCallback(new DisplayModeAction(this, DISPLAY_AIRC)); + AddComponent(tCheckbox); + + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4+18), ui::Point(30, 16), "Pressure", "Displays pressure, red is positive and blue is negative"); + displayModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconPressure); + tCheckbox->SetActionCallback(new DisplayModeAction(this, DISPLAY_AIRP)); + AddComponent(tCheckbox); + + checkboxOffset += cSpace; + + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Velocity", "Displays velocity and positive pressure: up/down adds blue, right/left adds red, still pressure adds green"); + displayModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconVelocity); + tCheckbox->SetActionCallback(new DisplayModeAction(this, DISPLAY_AIRV)); + AddComponent(tCheckbox); + + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4+18), ui::Point(30, 16), "Air-heat", "Displays the temperature of the air like heat display does"); + displayModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconHeat); + tCheckbox->SetActionCallback(new DisplayModeAction(this, DISPLAY_AIRH)); + AddComponent(tCheckbox); + + /*tCheckbox = new ui::Checkbox(ui::Point(216, YRES+4), ui::Point(30, 16), "Air", ""); + displayModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconAltAir); + tCheckbox->SetActionCallback(new DisplayModeAction(this, DISPLAY_AIR)); + AddComponent(tCheckbox);*/ + + checkboxOffset += sSpace; + line2 = checkboxOffset-5; + + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4+18), ui::Point(30, 16), "Warp", "Gravity lensing, Newtonian Gravity bends light with this on"); + displayModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconWarp); + tCheckbox->SetActionCallback(new DisplayModeAction(this, DISPLAY_WARP)); + AddComponent(tCheckbox); + +#ifdef OGLR + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Effect", "I don't know what this does..."); //I would remove the whole checkbox, but then there's a large empty space +#else + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Effect", "Does nothing"); +#endif + displayModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconEffect); + tCheckbox->SetActionCallback(new DisplayModeAction(this, DISPLAY_EFFE)); + AddComponent(tCheckbox); + + checkboxOffset += cSpace; + + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Persistent", "Element paths persist on the screen for a while"); + displayModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconPersistant); + tCheckbox->SetActionCallback(new DisplayModeAction(this, DISPLAY_PERS)); + AddComponent(tCheckbox); + + checkboxOffset += sSpace; + line3 = checkboxOffset-5; + + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Heat", "Displays temperatures of the elements, dark blue is coldest, pink is hottest"); + colourModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconHeat); + tCheckbox->SetActionCallback(new ColourModeAction(this, COLOUR_HEAT)); + AddComponent(tCheckbox); + + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4+18), ui::Point(30, 16), "Life", "Displays the life value of elements in greyscale gradients"); + colourModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconLife); + tCheckbox->SetActionCallback(new ColourModeAction(this, COLOUR_LIFE)); + AddComponent(tCheckbox); + + checkboxOffset += cSpace; + + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4+18), ui::Point(30, 16), "H-Gradient", "Changes colors of elements slightly to show heat diffusing through them"); + colourModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconGradient); + tCheckbox->SetActionCallback(new ColourModeAction(this, COLOUR_GRAD)); + AddComponent(tCheckbox); + + tCheckbox = new ui::Checkbox(ui::Point(checkboxOffset, YRES+4), ui::Point(30, 16), "Basic", "No special effects at all for anything, overrides all other options and deco"); + colourModes.push_back(tCheckbox); + tCheckbox->SetIcon(IconBasic); + tCheckbox->SetActionCallback(new ColourModeAction(this, COLOUR_BASC)); + AddComponent(tCheckbox); + + checkboxOffset += sSpace; + line4 = checkboxOffset-5; +} + +void RenderView::OnMouseDown(int x, int y, unsigned button) +{ + if(x > XRES || y < YRES) + c->Exit(); +} + +void RenderView::NotifyRendererChanged(RenderModel * sender) +{ + ren = sender->GetRenderer(); +} + +void RenderView::NotifyRenderChanged(RenderModel * sender) +{ + for(int i = 0; i < renderModes.size(); i++) + { + if(renderModes[i]->GetActionCallback()) + { + //Compares bitmasks at the moment, this means that "Point" is always on when other options that depend on it are, this might confuse some users, TODO: get the full list and compare that? + RenderModeAction * action = (RenderModeAction *)(renderModes[i]->GetActionCallback()); + if(action->renderMode == (sender->GetRenderMode() & action->renderMode)) + { + renderModes[i]->SetChecked(true); + } + else + { + renderModes[i]->SetChecked(false); + } + } + } +} + +void RenderView::NotifyDisplayChanged(RenderModel * sender) +{ + for(int i = 0; i < displayModes.size(); i++) + { + if(displayModes[i]->GetActionCallback()) + { + DisplayModeAction * action = (DisplayModeAction *)(displayModes[i]->GetActionCallback()); + if(action->displayMode == (sender->GetDisplayMode() & action->displayMode)) + { + displayModes[i]->SetChecked(true); + } + else + { + displayModes[i]->SetChecked(false); + } + } + } +} + +void RenderView::NotifyColourChanged(RenderModel * sender) +{ + for(int i = 0; i < colourModes.size(); i++) + { + if(colourModes[i]->GetActionCallback()) + { + ColourModeAction * action = (ColourModeAction *)(colourModes[i]->GetActionCallback()); + if(action->colourMode == sender->GetColourMode()) + { + colourModes[i]->SetChecked(true); + } + else + { + colourModes[i]->SetChecked(false); + } + } + } +} + +void RenderView::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + g->clearrect(-1, -1, XRES+BARSIZE+1, YRES+MENUSIZE+1); + if(ren) + { + ren->clearScreen(1.0f); + ren->RenderBegin(); + ren->RenderEnd(); + } + g->draw_line(0, YRES, XRES-1, YRES, 200, 200, 200, 255); + g->draw_line(line1, YRES, line1, YRES+MENUSIZE, 200, 200, 200, 255); + g->draw_line(line2, YRES, line2, YRES+MENUSIZE, 200, 200, 200, 255); + g->draw_line(line3, YRES, line3, YRES+MENUSIZE, 200, 200, 200, 255); + g->draw_line(line4, YRES, line4, YRES+MENUSIZE, 200, 200, 200, 255); + g->draw_line(XRES, 0, XRES, YRES+MENUSIZE, 255, 255, 255, 255); + if(toolTipPresence && toolTip.length()) + { + g->drawtext(6, Size.Y-MENUSIZE-12, (char*)toolTip.c_str(), 255, 255, 255, toolTipPresence>51?255:toolTipPresence*5); + } +} + +void RenderView::OnTick(float dt) +{ + if(toolTipPresence>0) + { + toolTipPresence -= int(dt)>0?int(dt):1; + if(toolTipPresence<0) + toolTipPresence = 0; + } +} + +void RenderView::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + if (shift && key == '1') + c->LoadRenderPreset(10); + else if(key >= '0' && key <= '9') + { + c->LoadRenderPreset(key-'0'); + } +} + +void RenderView::ToolTip(ui::Component * sender, ui::Point mousePosition, std::string toolTip) +{ + this->toolTip = toolTip; + toolTipPresence = 500; +} + +RenderView::~RenderView() { +} diff --git a/src/gui/render/RenderView.h b/src/gui/render/RenderView.h new file mode 100644 index 0000000..8a65eb1 --- /dev/null +++ b/src/gui/render/RenderView.h @@ -0,0 +1,43 @@ +#ifndef RENDERVIEW_H_ +#define RENDERVIEW_H_ + + +#include <vector> +#include "gui/interface/Window.h" +#include "RenderController.h" +#include "RenderModel.h" +#include "graphics/Renderer.h" +#include "gui/interface/Checkbox.h" +#include "gui/interface/Button.h" + +class RenderController; +class RenderModel; +class RenderView: public ui::Window { + RenderController * c; + Renderer * ren; + std::vector<ui::Checkbox*> renderModes; + std::vector<ui::Checkbox*> displayModes; + std::vector<ui::Checkbox*> colourModes; + std::string toolTip; + int toolTipPresence; + int line1, line2, line3, line4; +public: + class RenderModeAction; + class DisplayModeAction; + class ColourModeAction; + class RenderPresetAction; + RenderView(); + void NotifyRendererChanged(RenderModel * sender); + void NotifyRenderChanged(RenderModel * sender); + void NotifyDisplayChanged(RenderModel * sender); + void NotifyColourChanged(RenderModel * sender); + void AttachController(RenderController * c_) { c = c_; } + void OnMouseDown(int x, int y, unsigned button); + virtual void OnDraw(); + virtual void OnTick(float dt); + virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + virtual void ToolTip(ui::Component * sender, ui::Point mousePosition, std::string toolTip); + virtual ~RenderView(); +}; + +#endif /* RENDERVIEW_H_ */ diff --git a/src/gui/save/LocalSaveActivity.cpp b/src/gui/save/LocalSaveActivity.cpp new file mode 100644 index 0000000..f494f09 --- /dev/null +++ b/src/gui/save/LocalSaveActivity.cpp @@ -0,0 +1,145 @@ +#include "LocalSaveActivity.h" +#include "gui/interface/Label.h" +#include "gui/interface/Textbox.h" +#include "gui/interface/Button.h" +#include "gui/search/Thumbnail.h" +#include "client/requestbroker/RequestBroker.h" +#include "gui/dialogues/ErrorMessage.h" +#include "gui/dialogues/ConfirmPrompt.h" +#include "client/Client.h" +#include "client/GameSave.h" +#include "gui/Style.h" + +class LocalSaveActivity::CancelAction: public ui::ButtonAction +{ + LocalSaveActivity * a; +public: + CancelAction(LocalSaveActivity * a) : a(a) {} + virtual void ActionCallback(ui::Button * sender) + { + a->Exit(); + } +}; + +class LocalSaveActivity::SaveAction: public ui::ButtonAction +{ + LocalSaveActivity * a; +public: + SaveAction(LocalSaveActivity * a) : a(a) {} + virtual void ActionCallback(ui::Button * sender) + { + a->Save(); + } +}; + +LocalSaveActivity::LocalSaveActivity(SaveFile save, FileSavedCallback * callback) : + WindowActivity(ui::Point(-1, -1), ui::Point(220, 200)), + thumbnail(NULL), + save(save), + callback(callback) +{ + ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 16), "Save to computer:"); + titleLabel->SetTextColour(style::Colour::InformationTitle); + titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(titleLabel); + + filenameField = new ui::Textbox(ui::Point(8, 25), ui::Point(Size.X-16, 16), save.GetDisplayName(), "[filename]"); + filenameField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + filenameField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + AddComponent(filenameField); + FocusComponent(filenameField); + + ui::Button * cancelButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X-75, 16), "Cancel"); + cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + cancelButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + cancelButton->Appearance.BorderInactive = ui::Colour(200, 200, 200); + cancelButton->SetActionCallback(new CancelAction(this)); + AddComponent(cancelButton); + SetCancelButton(cancelButton); + + ui::Button * okayButton = new ui::Button(ui::Point(Size.X-76, Size.Y-16), ui::Point(76, 16), "Save"); + okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + okayButton->Appearance.TextInactive = style::Colour::InformationTitle; + okayButton->SetActionCallback(new SaveAction(this)); + AddComponent(okayButton); + SetOkayButton(okayButton); + + if(save.GetGameSave()) + RequestBroker::Ref().RenderThumbnail(save.GetGameSave(), true, false, Size.X-16, -1, this); +} + +void LocalSaveActivity::Save() +{ + class FileOverwriteConfirmation: public ConfirmDialogueCallback { + public: + LocalSaveActivity * a; + std::string filename; + FileOverwriteConfirmation(LocalSaveActivity * a, std::string finalFilename) : a(a), filename(finalFilename) {} + virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) { + if (result == ConfirmPrompt::ResultOkay) + { + a->saveWrite(filename); + a->Exit(); + } + } + virtual ~FileOverwriteConfirmation() { } + }; + + if(filenameField->GetText().length()) + { + std::string finalFilename = std::string(LOCAL_SAVE_DIR) + std::string(PATH_SEP) + filenameField->GetText() + ".cps"; + save.SetDisplayName(filenameField->GetText()); + save.SetFileName(finalFilename); + if(Client::Ref().FileExists(finalFilename)) + { + new ConfirmPrompt("Overwrite file", "Are you sure you wish to overwrite\n"+finalFilename, new FileOverwriteConfirmation(this, finalFilename)); + } + else + { + saveWrite(finalFilename); + Exit(); + } + } + else + { + new ErrorMessage("Error", "You must specify a filename."); + } +} + +void LocalSaveActivity::saveWrite(std::string finalFilename) +{ + Client::Ref().MakeDirectory(LOCAL_SAVE_DIR); + Client::Ref().WriteFile(save.GetGameSave()->Serialise(), finalFilename); + callback->FileSaved(&save); +} + +void LocalSaveActivity::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + + if(thumbnail) + { + g->draw_image(thumbnail, Position.X+(Size.X-thumbnail->Width)/2, Position.Y+45, 255); + g->drawrect(Position.X+(Size.X-thumbnail->Width)/2, Position.Y+45, thumbnail->Width, thumbnail->Height, 180, 180, 180, 255); + } +} + +void LocalSaveActivity::OnResponseReady(void * imagePtr) +{ + if(thumbnail) + delete thumbnail; + thumbnail = (VideoBuffer*)imagePtr; +} + +LocalSaveActivity::~LocalSaveActivity() +{ + RequestBroker::Ref().DetachRequestListener(this); + if(thumbnail) + delete thumbnail; + if(callback) + delete callback; +}
\ No newline at end of file diff --git a/src/gui/save/LocalSaveActivity.h b/src/gui/save/LocalSaveActivity.h new file mode 100644 index 0000000..b58af5e --- /dev/null +++ b/src/gui/save/LocalSaveActivity.h @@ -0,0 +1,39 @@ +#pragma once + +#include "Activity.h" +#include "client/SaveFile.h" +#include "client/requestbroker/RequestListener.h" + +namespace ui +{ + class Textbox; +} + +class VideoBuffer; + +class FileSavedCallback +{ +public: + FileSavedCallback() {} + virtual ~FileSavedCallback() {} + virtual void FileSaved(SaveFile * file) {} +}; + +class LocalSaveActivity: public WindowActivity, public RequestListener +{ + SaveFile save; + VideoBuffer * thumbnail; + ui::Textbox * filenameField; + class CancelAction; + class SaveAction; + friend class CancelAction; + friend class SaveAction; + FileSavedCallback * callback; +public: + LocalSaveActivity(SaveFile save, FileSavedCallback * callback); + void saveWrite(std::string finalFilename); + virtual void Save(); + virtual void OnDraw(); + virtual void OnResponseReady(void * imagePtr); + virtual ~LocalSaveActivity(); +};
\ No newline at end of file diff --git a/src/gui/save/ServerSaveActivity.cpp b/src/gui/save/ServerSaveActivity.cpp new file mode 100644 index 0000000..e8dfc1d --- /dev/null +++ b/src/gui/save/ServerSaveActivity.cpp @@ -0,0 +1,311 @@ +#include "ServerSaveActivity.h" +#include "gui/interface/Label.h" +#include "gui/interface/Textbox.h" +#include "gui/interface/Button.h" +#include "gui/interface/Checkbox.h" +#include "client/requestbroker/RequestBroker.h" +#include "gui/dialogues/ErrorMessage.h" +#include "gui/dialogues/ConfirmPrompt.h" +#include "gui/dialogues/InformationMessage.h" +#include "client/Client.h" +#include "tasks/Task.h" +#include "gui/Style.h" +#include "client/GameSave.h" + +class ServerSaveActivity::CancelAction: public ui::ButtonAction +{ + ServerSaveActivity * a; +public: + CancelAction(ServerSaveActivity * a) : a(a) {} + virtual void ActionCallback(ui::Button * sender) + { + a->Exit(); + } +}; + +class ServerSaveActivity::SaveAction: public ui::ButtonAction +{ + ServerSaveActivity * a; +public: + SaveAction(ServerSaveActivity * a) : a(a) {} + virtual void ActionCallback(ui::Button * sender) + { + a->Save(); + } +}; + +class ServerSaveActivity::RulesAction: public ui::ButtonAction +{ + ServerSaveActivity * a; +public: + RulesAction(ServerSaveActivity * a) : a(a) {} + virtual void ActionCallback(ui::Button * sender) + { + a->ShowRules(); + } +}; + +class SaveUploadTask: public Task +{ + SaveInfo save; + + virtual void before() + { + + } + + virtual void after() + { + + } + + virtual bool doWork() + { + notifyProgress(-1); + return Client::Ref().UploadSave(save) == RequestOkay; + } + +public: + SaveInfo GetSave() + { + return save; + } + + SaveUploadTask(SaveInfo save): + save(save) + { + + } +}; + +ServerSaveActivity::ServerSaveActivity(SaveInfo save, ServerSaveActivity::SaveUploadedCallback * callback) : + WindowActivity(ui::Point(-1, -1), ui::Point(440, 200)), + thumbnail(NULL), + save(save), + callback(callback), + saveUploadTask(NULL) +{ + ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point((Size.X/2)-8, 16), "Save to server:"); + titleLabel->SetTextColour(style::Colour::InformationTitle); + titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(titleLabel); + + ui::Label * previewLabel = new ui::Label(ui::Point((Size.X/2)+4, 5), ui::Point((Size.X/2)-8, 16), "Preview:"); + previewLabel->SetTextColour(style::Colour::InformationTitle); + previewLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + previewLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(previewLabel); + + nameField = new ui::Textbox(ui::Point(8, 25), ui::Point((Size.X/2)-16, 16), save.GetName(), "[save name]"); + nameField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + nameField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + AddComponent(nameField); + + descriptionField = new ui::Textbox(ui::Point(8, 65), ui::Point((Size.X/2)-16, Size.Y-(65+16+4)), save.GetDescription(), "[save description]"); + descriptionField->SetMultiline(true); + descriptionField->SetLimit(254); + descriptionField->Appearance.VerticalAlign = ui::Appearance::AlignTop; + descriptionField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + AddComponent(descriptionField); + + publishedCheckbox = new ui::Checkbox(ui::Point(8, 45), ui::Point((Size.X/2)-80, 16), "Publish", ""); + if(Client::Ref().GetAuthUser().Username != save.GetUserName()) + { + //Save is not owned by the user, disable by default + publishedCheckbox->SetChecked(false); + } + else + { + //Save belongs to the current user, use published state already set + publishedCheckbox->SetChecked(save.GetPublished()); + } + AddComponent(publishedCheckbox); + + pausedCheckbox = new ui::Checkbox(ui::Point(160, 45), ui::Point(55, 16), "Paused", ""); + pausedCheckbox->SetChecked(save.GetGameSave()->paused); + AddComponent(pausedCheckbox); + + ui::Button * cancelButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point((Size.X/2)-75, 16), "Cancel"); + cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + cancelButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + cancelButton->Appearance.BorderInactive = ui::Colour(200, 200, 200); + cancelButton->SetActionCallback(new CancelAction(this)); + AddComponent(cancelButton); + SetCancelButton(cancelButton); + + ui::Button * okayButton = new ui::Button(ui::Point((Size.X/2)-76, Size.Y-16), ui::Point(76, 16), "Save"); + okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + okayButton->Appearance.TextInactive = style::Colour::InformationTitle; + okayButton->SetActionCallback(new SaveAction(this)); + AddComponent(okayButton); + SetOkayButton(okayButton); + + ui::Button * RulesButton = new ui::Button(ui::Point((Size.X*3/4)-75, Size.Y-20), ui::Point(150, 16), "Save Uploading Rules"); + RulesButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; + RulesButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + RulesButton->Appearance.TextInactive = style::Colour::InformationTitle; + RulesButton->SetActionCallback(new RulesAction(this)); + AddComponent(RulesButton); + + if(save.GetGameSave()) + RequestBroker::Ref().RenderThumbnail(save.GetGameSave(), false, true, (Size.X/2)-16, -1, this); +} + +ServerSaveActivity::ServerSaveActivity(SaveInfo save, bool saveNow, ServerSaveActivity::SaveUploadedCallback * callback) : + WindowActivity(ui::Point(-1, -1), ui::Point(200, 50)), + thumbnail(NULL), + save(save), + callback(callback), + saveUploadTask(NULL) +{ + ui::Label * titleLabel = new ui::Label(ui::Point(0, 0), Size, "Saving to server..."); + titleLabel->SetTextColour(style::Colour::InformationTitle); + titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; + titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(titleLabel); + + saveUploadTask = new SaveUploadTask(save); + saveUploadTask->AddTaskListener(this); + saveUploadTask->Start(); +} + +void ServerSaveActivity::NotifyDone(Task * task) +{ + if(!task->GetSuccess()) + { + Exit(); + new ErrorMessage("Error", Client::Ref().GetLastError()); + } + else + { + if(callback) + { + callback->SaveUploaded(save); + } + Exit(); + } +} + +void ServerSaveActivity::Save() +{ + class PublishConfirmation: public ConfirmDialogueCallback { + public: + ServerSaveActivity * a; + PublishConfirmation(ServerSaveActivity * a) : a(a) {} + virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) { + if (result == ConfirmPrompt::ResultOkay) + { + a->Exit(); + a->saveUpload(); + } + } + virtual ~PublishConfirmation() { } + }; + + if(nameField->GetText().length()) + { + if(Client::Ref().GetAuthUser().Username != save.GetUserName() && publishedCheckbox->GetChecked()) + { + new ConfirmPrompt("Publish", "This save was created by " + save.GetUserName() + ", you're about to publish this under your own name; If you haven't been given permission by the author to do so, please uncheck the publish box, otherwise continue", new PublishConfirmation(this)); + } + else + { + Exit(); + saveUpload(); + } + } + else + { + new ErrorMessage("Error", "You must specify a save name."); + } +} + +void ServerSaveActivity::saveUpload() +{ + save.SetName(nameField->GetText()); + save.SetDescription(descriptionField->GetText()); + save.SetPublished(publishedCheckbox->GetChecked()); + save.SetUserName(Client::Ref().GetAuthUser().Username); + save.SetID(0); + save.GetGameSave()->paused = pausedCheckbox->GetChecked(); + + if(Client::Ref().UploadSave(save) != RequestOkay) + { + new ErrorMessage("Error", "Upload failed with error:\n"+Client::Ref().GetLastError()); + } + else if(callback) + { + callback->SaveUploaded(save); + } +} + +void ServerSaveActivity::Exit() +{ + WindowActivity::Exit(); +} + +void ServerSaveActivity::ShowRules() +{ + const char *rules = + "These are the rules you should follow when uploading saves. They may change at any time as new problems arise, and how each rule is handled changes depending on the situation.\n" + "\n" + "\bt1. No image plotting.\bw If you use a program to draw out pixels from an image outside of TPT without drawing it by hand, then don't be surprised when it gets deleted and you get banned.\n" + "\bt2. No self voting.\bw This means making more than one account, and then using that account to vote on any save multiple times. We can see this stuff, and people get banned for doing this. Don't do it.\n" + "\bt3. No hate saves.\bw This means things like shooting Jews or killing Beiber, these will not be allowed.\n" + "\bt4. No penis drawings.\bw Or any other explicit or non-explicit sex please. We like to think this is a game that kids can play, don't post anything too inappropriate for children.\n" + "\bt5. Don't ask people to vote.\bw If your stuff is awesome, you shouldn't have to beg for popularity to make it so. People tend to downvote when they see a lot of vote begging anyway.\n" + "- This includes vote signs in the game, drawings of vote arrows, and comments on the save telling people to vote up.\n" + "- Gimmicks for getting votes like '100 votes and I'll make a better version' are similarly frowned upon.\n" + "\bt6. Keep the number of logos and signs to a minimum.\bw They not only slow the game down, but it makes saves unappealing for people to use. \n" + "\bt7. Please don't swear excessively.\bw Saves containing excessive swearing or rude language will be unpublished. Don't make rude or offensive comments either.\n" + "\bt8. Don't make text only saves.\bw Saves are much better when they actually use some of the features in the game. Text only saves will be removed from the front page if they should get there.\n" + "- This also relates to art on the front page. Art saves that rely only on the deco layer are generally removed. Other art using elements may stay longer if they are more impressive.\n" + "\bt9. Don't claim other's work as your own.\bw If you didn't make it, don't resave it for yourself. You can fav. a save if you want to see it later instead of publishing a copy.\n" + "- This doesn't mean you can't modify or improve saves, building on the works of others in encouraged. If you give credit to the original author, it is usually ok to do resave unless the author specifically prohibits it.\n" + "\n" + "You can report a save breaking any one of these rules, the moderators are busy in real life too and don't always have the time to search through all the saves for these kinds of things. If reporting a copied save, just give the id of the original, but if not an id isn't needed."; + + new InformationMessage("Save Uploading Rules", rules, true); +} + +void ServerSaveActivity::OnTick(float dt) +{ + if(saveUploadTask) + saveUploadTask->Poll(); +} + +void ServerSaveActivity::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + + if(Size.X>220) + g->draw_line(Position.X+(Size.X/2)-1, Position.Y, Position.X+(Size.X/2)-1, Position.Y+Size.Y-1, 255, 255, 255, 255); + + if(thumbnail) + { + g->draw_image(thumbnail, Position.X+(Size.X/2)+((Size.X/2)-thumbnail->Width)/2, Position.Y+25, 255); + g->drawrect(Position.X+(Size.X/2)+((Size.X/2)-thumbnail->Width)/2, Position.Y+25, thumbnail->Width, thumbnail->Height, 180, 180, 180, 255); + } +} + +void ServerSaveActivity::OnResponseReady(void * imagePtr) +{ + if(thumbnail) + delete thumbnail; + thumbnail = (VideoBuffer *)imagePtr; +} + +ServerSaveActivity::~ServerSaveActivity() +{ + RequestBroker::Ref().DetachRequestListener(this); + if(saveUploadTask) + delete saveUploadTask; + if(callback) + delete callback; + if(thumbnail) + delete thumbnail; +}
\ No newline at end of file diff --git a/src/gui/save/ServerSaveActivity.h b/src/gui/save/ServerSaveActivity.h new file mode 100644 index 0000000..793579b --- /dev/null +++ b/src/gui/save/ServerSaveActivity.h @@ -0,0 +1,52 @@ +#pragma once + +#include "Activity.h" +#include "client/SaveInfo.h" +#include "client/requestbroker/RequestListener.h" +#include "tasks/TaskListener.h" + +namespace ui +{ + class Textbox; + class Checkbox; +} + +class Task; +class Thumbnail; +class ServerSaveActivity: public WindowActivity, public RequestListener, public TaskListener +{ +public: + class SaveUploadedCallback + { + public: + SaveUploadedCallback() {} + virtual ~SaveUploadedCallback() {} + virtual void SaveUploaded(SaveInfo save) {} + }; + ServerSaveActivity(SaveInfo save, SaveUploadedCallback * callback); + ServerSaveActivity(SaveInfo save, bool saveNow, SaveUploadedCallback * callback); + void saveUpload(); + virtual void Save(); + virtual void Exit(); + virtual void ShowRules(); + virtual void OnDraw(); + virtual void OnResponseReady(void * imagePtr); + virtual void OnTick(float dt); + virtual ~ServerSaveActivity(); +protected: + virtual void NotifyDone(Task * task); + Task * saveUploadTask; + SaveUploadedCallback * callback; + SaveInfo save; + VideoBuffer * thumbnail; + ui::Textbox * nameField; + ui::Textbox * descriptionField; + ui::Checkbox * publishedCheckbox; + ui::Checkbox * pausedCheckbox; + class CancelAction; + class SaveAction; + class RulesAction; + friend class CancelAction; + friend class SaveAction; + friend class RulesAction; +};
\ No newline at end of file diff --git a/src/gui/search/SearchController.cpp b/src/gui/search/SearchController.cpp new file mode 100644 index 0000000..b881962 --- /dev/null +++ b/src/gui/search/SearchController.cpp @@ -0,0 +1,364 @@ +#include <string> +#include <sstream> +#include <time.h> +#include "SearchController.h" +#include "SearchModel.h" +#include "SearchView.h" +#include "gui/interface/Panel.h" +#include "gui/dialogues/ConfirmPrompt.h" +#include "gui/dialogues/ErrorMessage.h" +#include "gui/preview/PreviewController.h" +#include "client/Client.h" +#include "tasks/Task.h" +#include "tasks/TaskWindow.h" + +class SearchController::OpenCallback: public ControllerCallback +{ + SearchController * cc; +public: + OpenCallback(SearchController * cc_) { cc = cc_; } + virtual void ControllerExit() + { + if(cc->activePreview->GetDoOpen() && cc->activePreview->GetSave()) + { + cc->searchModel->SetLoadedSave(cc->activePreview->GetSave()); + } + else + { + cc->searchModel->SetLoadedSave(NULL); + } + + } +}; + +SearchController::SearchController(ControllerCallback * callback): + activePreview(NULL), + HasExited(false), + nextQueryTime(0.0f), + nextQueryDone(true), + searchModel(NULL) +{ + searchModel = new SearchModel(); + searchView = new SearchView(); + searchModel->AddObserver(searchView); + searchView->AttachController(this); + + searchModel->UpdateSaveList(1, ""); + + this->callback = callback; + + //Set up interface + //windowPanel.AddChild(); +} + +SaveInfo * SearchController::GetLoadedSave() +{ + return searchModel->GetLoadedSave(); +} + +void SearchController::ReleaseLoadedSave() +{ + searchModel->SetLoadedSave(NULL); +} + +void SearchController::Update() +{ + if(!nextQueryDone && nextQueryTime < clock()) + { + nextQueryDone = true; + searchModel->UpdateSaveList(1, nextQuery); + } + searchModel->Update(); + if(activePreview && activePreview->HasExited) + { + delete activePreview; + activePreview = NULL; + if(searchModel->GetLoadedSave()) + { + Exit(); + } + } +} + +void SearchController::Exit() +{ + if(ui::Engine::Ref().GetWindow() == searchView) + { + ui::Engine::Ref().CloseWindow(); + } + if(callback) + callback->ControllerExit(); + //HasExited = true; +} + +SearchController::~SearchController() +{ + if(activePreview) + delete activePreview; + if(ui::Engine::Ref().GetWindow() == searchView) + { + ui::Engine::Ref().CloseWindow(); + } + delete searchModel; + delete searchView; + delete callback; +} + +void SearchController::DoSearch(std::string query, bool now) +{ + nextQuery = query; + if(!now) + { + nextQueryTime = clock()+(0.6 * CLOCKS_PER_SEC); + nextQueryDone = false; + } + else + { + nextQueryDone = true; + searchModel->UpdateSaveList(1, nextQuery); + } + //searchModel->UpdateSaveList(1, query); +} + +void SearchController::PrevPage() +{ + if(searchModel->GetPageNum()>1) + searchModel->UpdateSaveList(searchModel->GetPageNum()-1, searchModel->GetLastQuery()); +} + +void SearchController::NextPage() +{ + if(searchModel->GetPageNum() <= searchModel->GetPageCount()) + searchModel->UpdateSaveList(searchModel->GetPageNum()+1, searchModel->GetLastQuery()); +} + +void SearchController::ChangeSort() +{ + if(searchModel->GetSort() == "new") + { + searchModel->SetSort("best"); + } + else + { + searchModel->SetSort("new"); + } + searchModel->UpdateSaveList(1, searchModel->GetLastQuery()); +} + +void SearchController::ShowOwn(bool show) +{ + if(Client::Ref().GetAuthUser().ID) + { + searchModel->SetShowFavourite(false); + searchModel->SetShowOwn(show); + } + else + searchModel->SetShowOwn(false); + searchModel->UpdateSaveList(1, searchModel->GetLastQuery()); +} + +void SearchController::ShowFavourite(bool show) +{ + if(Client::Ref().GetAuthUser().ID) + { + searchModel->SetShowOwn(false); + searchModel->SetShowFavourite(show); + } + else + searchModel->SetShowFavourite(false); + searchModel->UpdateSaveList(1, searchModel->GetLastQuery()); +} + +void SearchController::Selected(int saveID, bool selected) +{ + if(!Client::Ref().GetAuthUser().ID) + return; + + if(selected) + searchModel->SelectSave(saveID); + else + searchModel->DeselectSave(saveID); +} + +void SearchController::OpenSave(int saveID) +{ + if(activePreview) + delete activePreview; + activePreview = new PreviewController(saveID, new OpenCallback(this)); + ui::Engine::Ref().ShowWindow(activePreview->GetView()); +} + +void SearchController::OpenSave(int saveID, int saveDate) +{ + if(activePreview) + delete activePreview; + activePreview = new PreviewController(saveID, saveDate, new OpenCallback(this)); + ui::Engine::Ref().ShowWindow(activePreview->GetView()); +} + +void SearchController::ClearSelection() +{ + searchModel->ClearSelected(); +} + +void SearchController::RemoveSelected() +{ + class RemoveSelectedConfirmation: public ConfirmDialogueCallback { + public: + SearchController * c; + RemoveSelectedConfirmation(SearchController * c_) { c = c_; } + virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) { + if (result == ConfirmPrompt::ResultOkay) + c->removeSelectedC(); + } + virtual ~RemoveSelectedConfirmation() { } + }; + + std::stringstream desc; + desc << "Are you sure you want to delete " << searchModel->GetSelected().size() << " save"; + if(searchModel->GetSelected().size()>1) + desc << "s"; + new ConfirmPrompt("Delete saves", desc.str(), new RemoveSelectedConfirmation(this)); +} + +void SearchController::removeSelectedC() +{ + class RemoveSavesTask : public Task + { + std::vector<int> saves; + public: + RemoveSavesTask(std::vector<int> saves_) { saves = saves_; } + virtual bool doWork() + { + for(int i = 0; i < saves.size(); i++) + { + std::stringstream saveID; + saveID << "Deleting save [" << saves[i] << "] ..."; + notifyStatus(saveID.str()); + if(Client::Ref().DeleteSave(saves[i])!=RequestOkay) + { + std::stringstream saveIDF; + saveIDF << "\boFailed to delete [" << saves[i] << "] ..."; + notifyStatus(saveIDF.str()); + } + notifyProgress((float(i+1)/float(saves.size())*100)); + } + return true; + } + }; + + std::vector<int> selected = searchModel->GetSelected(); + new TaskWindow("Removing saves", new RemoveSavesTask(selected)); + ClearSelection(); + searchModel->UpdateSaveList(searchModel->GetPageNum(), searchModel->GetLastQuery()); +} + +void SearchController::UnpublishSelected() +{ + class UnpublishSelectedConfirmation: public ConfirmDialogueCallback { + public: + SearchController * c; + UnpublishSelectedConfirmation(SearchController * c_) { c = c_; } + virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) { + if (result == ConfirmPrompt::ResultOkay) + c->unpublishSelectedC(); + } + virtual ~UnpublishSelectedConfirmation() { } + }; + + std::stringstream desc; + desc << "Are you sure you want to hide " << searchModel->GetSelected().size() << " save"; + if(searchModel->GetSelected().size()>1) + desc << "s"; + new ConfirmPrompt("Unpublish saves", desc.str(), new UnpublishSelectedConfirmation(this)); +} + +void SearchController::unpublishSelectedC() +{ + class UnpublishSavesTask : public Task + { + std::vector<int> saves; + public: + UnpublishSavesTask(std::vector<int> saves_) { saves = saves_; } + virtual bool doWork() + { + for(int i = 0; i < saves.size(); i++) + { + std::stringstream saveID; + saveID << "Hiding save [" << saves[i] << "] ..."; + notifyStatus(saveID.str()); + if(Client::Ref().UnpublishSave(saves[i])!=RequestOkay) + { + std::stringstream saveIDF; + saveIDF << "\boFailed to hide [" << saves[i] << "] ..."; + notifyStatus(saveIDF.str()); + } + notifyProgress((float(i+1)/float(saves.size())*100)); + } + return true; + } + }; + + std::vector<int> selected = searchModel->GetSelected(); + new TaskWindow("Unpublishing saves", new UnpublishSavesTask(selected)); + ClearSelection(); + searchModel->UpdateSaveList(searchModel->GetPageNum(), searchModel->GetLastQuery()); +} + +void SearchController::FavouriteSelected() +{ + class FavouriteSavesTask : public Task + { + std::vector<int> saves; + public: + FavouriteSavesTask(std::vector<int> saves_) { saves = saves_; } + virtual bool doWork() + { + for(int i = 0; i < saves.size(); i++) + { + std::stringstream saveID; + saveID << "Favouring save [" << saves[i] << "] ..."; + notifyStatus(saveID.str()); + if(Client::Ref().FavouriteSave(saves[i], true)!=RequestOkay) + { + std::stringstream saveIDF; + saveIDF << "\boFailed to favourite [" << saves[i] << "] ..."; + notifyStatus(saveIDF.str()); + } + notifyProgress((float(i+1)/float(saves.size())*100)); + } + return true; + } + }; + + class UnfavouriteSavesTask : public Task + { + std::vector<int> saves; + public: + UnfavouriteSavesTask(std::vector<int> saves_) { saves = saves_; } + virtual bool doWork() + { + for(int i = 0; i < saves.size(); i++) + { + std::stringstream saveID; + saveID << "Unfavouring save [" << saves[i] << "] ..."; + notifyStatus(saveID.str()); + if(Client::Ref().FavouriteSave(saves[i], false)!=RequestOkay) + { + std::stringstream saveIDF; + saveIDF << "\boFailed to remove [" << saves[i] << "] ..."; + notifyStatus(saveIDF.str()); + } + notifyProgress((float(i+1)/float(saves.size())*100)); + } + return true; + } + }; + + std::vector<int> selected = searchModel->GetSelected(); + if(!searchModel->GetShowFavourite()) + new TaskWindow("Favouring saves", new FavouriteSavesTask(selected)); + else + new TaskWindow("Unfavouring saves", new UnfavouriteSavesTask(selected)); + ClearSelection(); +} diff --git a/src/gui/search/SearchController.h b/src/gui/search/SearchController.h new file mode 100644 index 0000000..8d811ab --- /dev/null +++ b/src/gui/search/SearchController.h @@ -0,0 +1,51 @@ +#ifndef SEARCHCONTROLLER_H +#define SEARCHCONTROLLER_H + +#include "gui/interface/Panel.h" +#include "SearchModel.h" +#include "SearchView.h" +#include "gui/preview/PreviewController.h" +#include "Controller.h" +#include "client/SaveInfo.h" + +class SearchView; +class SearchModel; +class SearchController +{ +private: + SearchModel * searchModel; + SearchView * searchView; + PreviewController * activePreview; + ControllerCallback * callback; + + double nextQueryTime; + std::string nextQuery; + bool nextQueryDone; + void removeSelectedC(); + void unpublishSelectedC(); +public: + class OpenCallback; + bool HasExited; + SearchController(ControllerCallback * callback = NULL); + ~SearchController(); + SearchView * GetView() { return searchView; } + void Exit(); + void DoSearch(std::string query, bool now = false); + void NextPage(); + void PrevPage(); + void ChangeSort(); + void ShowOwn(bool show); + void ShowFavourite(bool show); + void Selected(int saveID, bool selected); + void OpenSave(int saveID); + void OpenSave(int saveID, int saveDate); + void Update(); + void ClearSelection(); + void RemoveSelected(); + void UnpublishSelected(); + void FavouriteSelected(); + void ReleaseLoadedSave(); + SaveInfo * GetLoadedSave(); +}; + +#endif // SEARCHCONTROLLER_H diff --git a/src/gui/search/SearchModel.cpp b/src/gui/search/SearchModel.cpp new file mode 100644 index 0000000..f4de2d6 --- /dev/null +++ b/src/gui/search/SearchModel.cpp @@ -0,0 +1,283 @@ +#include "SearchModel.h" +#include "client/SaveInfo.h" + +#include "client/Client.h" + +SearchModel::SearchModel(): + currentSort("best"), + showOwn(false), + showFavourite(false), + loadedSave(NULL), + updateSaveListWorking(false), + updateSaveListFinished(false), + updateTagListWorking(false), + updateTagListFinished(false), + saveListLoaded(false), + currentPage(1), + resultCount(0), + showTags(true) +{ +} + +void SearchModel::SetShowTags(bool show) +{ + showTags = show; +} + +bool SearchModel::GetShowTags() +{ + return showTags; +} + +void * SearchModel::updateSaveListTHelper(void * obj) +{ + return ((SearchModel *)obj)->updateSaveListT(); +} + +void * SearchModel::updateSaveListT() +{ + std::string category = ""; + if(showFavourite) + category = "Favourites"; + if(showOwn && Client::Ref().GetAuthUser().ID) + category = "by:"+Client::Ref().GetAuthUser().Username; + vector<SaveInfo*> * saveList = Client::Ref().SearchSaves((currentPage-1)*20, 20, lastQuery, currentSort=="new"?"date":"votes", category, thResultCount); + + updateSaveListFinished = true; + return saveList; +} + +void * SearchModel::updateTagListTHelper(void * obj) +{ + return ((SearchModel *)obj)->updateTagListT(); +} + +void * SearchModel::updateTagListT() +{ + int tagResultCount; + std::vector<std::pair<std::string, int> > * tagList = Client::Ref().GetTags(0, 24, "", tagResultCount); + + updateTagListFinished = true; + return tagList; +} + +void SearchModel::UpdateSaveList(int pageNumber, std::string query) +{ + //Threading + if(!updateSaveListWorking) + { + lastQuery = query; + lastError = ""; + saveListLoaded = false; + saveList.clear(); + //resultCount = 0; + currentPage = pageNumber; + + if(pageNumber == 1 && !showOwn && !showFavourite && currentSort == "best" && query == "") + SetShowTags(true); + else + SetShowTags(false); + + notifySaveListChanged(); + notifyTagListChanged(); + notifyPageChanged(); + selected.clear(); + notifySelectedChanged(); + + if(GetShowTags() && !tagList.size() && !updateTagListWorking) + { + updateTagListFinished = false; + updateTagListWorking = true; + pthread_create(&updateTagListThread, 0, &SearchModel::updateTagListTHelper, this); + } + + updateSaveListFinished = false; + updateSaveListWorking = true; + pthread_create(&updateSaveListThread, 0, &SearchModel::updateSaveListTHelper, this); + } +} + +void SearchModel::SetLoadedSave(SaveInfo * save) +{ + if(loadedSave != save && loadedSave) + delete loadedSave; + if(save) + { + loadedSave = new SaveInfo(*save); + } + else + { + loadedSave = NULL; + } +} + +SaveInfo * SearchModel::GetLoadedSave(){ + return loadedSave; +} + +vector<SaveInfo*> SearchModel::GetSaveList() +{ + return saveList; +} + +vector<pair<string, int> > SearchModel::GetTagList() +{ + return tagList; +} + +void SearchModel::Update() +{ + if(updateSaveListWorking) + { + if(updateSaveListFinished) + { + updateSaveListWorking = false; + lastError = ""; + saveListLoaded = true; + + vector<SaveInfo*> * tempSaveList; + pthread_join(updateSaveListThread, (void**)&tempSaveList); + + if(tempSaveList) + { + saveList = *tempSaveList; + delete tempSaveList; + } + + if(!saveList.size()) + { + lastError = Client::Ref().GetLastError(); + } + + resultCount = thResultCount; + notifyPageChanged(); + notifySaveListChanged(); + } + } + if(updateTagListWorking) + { + if(updateTagListFinished) + { + updateTagListWorking = false; + + vector<pair<string, int> > * tempTagList; + pthread_join(updateTagListThread, (void**)&tempTagList); + + if(tempTagList) + { + tagList = *tempTagList; + delete tempTagList; + } + notifyTagListChanged(); + } + } +} + +void SearchModel::AddObserver(SearchView * observer) +{ + observers.push_back(observer); + observer->NotifySaveListChanged(this); + observer->NotifyPageChanged(this); + observer->NotifySortChanged(this); + observer->NotifyShowOwnChanged(this); + observer->NotifyTagListChanged(this); +} + +void SearchModel::SelectSave(int saveID) +{ + for(int i = 0; i < selected.size(); i++) + { + if(selected[i]==saveID) + { + return; + } + } + selected.push_back(saveID); + notifySelectedChanged(); +} + +void SearchModel::DeselectSave(int saveID) +{ + bool changed = false; +restart: + for(int i = 0; i < selected.size(); i++) + { + if(selected[i]==saveID) + { + selected.erase(selected.begin()+i); + changed = true; + goto restart; //Just ensure all cases are removed. + } + } + if(changed) + notifySelectedChanged(); +} + +void SearchModel::notifySaveListChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + SearchView* cObserver = observers[i]; + cObserver->NotifySaveListChanged(this); + } +} + +void SearchModel::notifyTagListChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + SearchView* cObserver = observers[i]; + cObserver->NotifyTagListChanged(this); + } +} + +void SearchModel::notifyPageChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + SearchView* cObserver = observers[i]; + cObserver->NotifyPageChanged(this); + } +} + +void SearchModel::notifySortChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + SearchView* cObserver = observers[i]; + cObserver->NotifySortChanged(this); + } +} + +void SearchModel::notifyShowOwnChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + SearchView* cObserver = observers[i]; + cObserver->NotifyShowOwnChanged(this); + } +} + +void SearchModel::notifyShowFavouriteChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + SearchView* cObserver = observers[i]; + cObserver->NotifyShowOwnChanged(this); + } +} + +void SearchModel::notifySelectedChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + SearchView* cObserver = observers[i]; + cObserver->NotifySelectedChanged(this); + } +} + +SearchModel::~SearchModel() +{ + if(loadedSave) + delete loadedSave; +} diff --git a/src/gui/search/SearchModel.h b/src/gui/search/SearchModel.h new file mode 100644 index 0000000..0fe9480 --- /dev/null +++ b/src/gui/search/SearchModel.h @@ -0,0 +1,83 @@ +#ifndef SEARCHMODEL_H +#define SEARCHMODEL_H + +#include <vector> +#include <string> +#include <pthread.h> +#undef GetUserName //God dammit microsoft! +#include <cmath> +#include "client/SaveInfo.h" +#include "SearchView.h" + +using namespace std; + +class SearchView; +class SearchModel +{ +private: + SaveInfo * loadedSave; + string currentSort; + string lastQuery; + string lastError; + vector<int> selected; + vector<SearchView*> observers; + vector<SaveInfo*> saveList; + vector<pair<string, int> > tagList; + int currentPage; + int resultCount; + int thResultCount; + bool showOwn; + bool showFavourite; + bool showTags; + void notifySaveListChanged(); + void notifyTagListChanged(); + void notifySelectedChanged(); + void notifyPageChanged(); + void notifySortChanged(); + void notifyShowOwnChanged(); + void notifyShowFavouriteChanged(); + + //Variables and methods for background save request + bool saveListLoaded; + bool updateSaveListWorking; + volatile bool updateSaveListFinished; + pthread_t updateSaveListThread; + static void * updateSaveListTHelper(void * obj); + void * updateSaveListT(); + + bool updateTagListWorking; + volatile bool updateTagListFinished; + pthread_t updateTagListThread; + static void * updateTagListTHelper(void * obj); + void * updateTagListT(); +public: + SearchModel(); + virtual ~SearchModel(); + + void SetShowTags(bool show); + bool GetShowTags(); + void AddObserver(SearchView * observer); + void UpdateSaveList(int pageNumber, std::string query); + vector<SaveInfo*> GetSaveList(); + vector<pair<string, int> > GetTagList(); + string GetLastError() { return lastError; } + int GetPageCount() { return max(1, (int)(ceil(resultCount/16.0f))); } + int GetPageNum() { return currentPage; } + std::string GetLastQuery() { return lastQuery; } + void SetSort(string sort) { if(!updateSaveListWorking) { currentSort = sort; } notifySortChanged(); } + string GetSort() { return currentSort; } + void SetShowOwn(bool show) { if(!updateSaveListWorking) { if(show!=showOwn) { showOwn = show; } } notifyShowOwnChanged(); } + bool GetShowOwn() { return showOwn; } + void SetShowFavourite(bool show) { if(show!=showFavourite && !updateSaveListWorking) { showFavourite = show; } notifyShowFavouriteChanged(); } + bool GetShowFavourite() { return showFavourite; } + void SetLoadedSave(SaveInfo * save); + SaveInfo * GetLoadedSave(); + bool GetSavesLoaded() { return saveListLoaded; } + vector<int> GetSelected() { return selected; } + void ClearSelected() { selected.clear(); notifySelectedChanged(); } + void SelectSave(int saveID); + void DeselectSave(int saveID); + void Update(); +}; + +#endif // SEARCHMODEL_H diff --git a/src/gui/search/SearchView.cpp b/src/gui/search/SearchView.cpp new file mode 100644 index 0000000..902be77 --- /dev/null +++ b/src/gui/search/SearchView.cpp @@ -0,0 +1,718 @@ +#include <sstream> + +#include "SearchView.h" +#include "client/Client.h" +#include "gui/interface/Keys.h" +#include "gui/interface/SaveButton.h" +#include "gui/interface/Label.h" +#include "gui/interface/RichLabel.h" +#include "gui/interface/Textbox.h" +#include "Misc.h" + +SearchView::SearchView(): + ui::Window(ui::Point(0, 0), ui::Point(XRES+BARSIZE, YRES+MENUSIZE)), + saveButtons(vector<ui::SaveButton*>()), + errorLabel(NULL), + c(NULL) +{ + + Client::Ref().AddListener(this); + + nextButton = new ui::Button(ui::Point(XRES+BARSIZE-52, YRES+MENUSIZE-18), ui::Point(50, 16), "Next \x95"); + previousButton = new ui::Button(ui::Point(1, YRES+MENUSIZE-18), ui::Point(50, 16), "\x96 Prev"); + infoLabel = new ui::Label(ui::Point(260, YRES+MENUSIZE-18), ui::Point(XRES+BARSIZE-520, 16), "Page 1 of 1"); + tagsLabel = new ui::Label(ui::Point(270, YRES+MENUSIZE-18), ui::Point(XRES+BARSIZE-540, 16), "\boPopular Tags:"); + motdLabel = new ui::RichLabel(ui::Point(51, YRES+MENUSIZE-18), ui::Point(XRES+BARSIZE-102, 16), Client::Ref().GetMessageOfTheDay()); + + class SearchAction : public ui::TextboxAction + { + SearchView * v; + public: + SearchAction(SearchView * _v) { v = _v; } + void TextChangedCallback(ui::Textbox * sender) + { + v->doSearch(); + } + }; + searchField = new ui::Textbox(ui::Point(60, 10), ui::Point((XRES+BARSIZE)-238, 17), "", "[search]"); + searchField->Appearance.icon = IconSearch; + searchField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + searchField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + searchField->SetActionCallback(new SearchAction(this)); + + + class SortAction : public ui::ButtonAction + { + SearchView * v; + public: + SortAction(SearchView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->ChangeSort(); + } + }; + sortButton = new ui::Button(ui::Point(XRES+BARSIZE-140, 10), ui::Point(61, 17), "Sort"); + sortButton->SetIcon(IconVoteSort); + sortButton->SetTogglable(true); + sortButton->SetActionCallback(new SortAction(this)); + sortButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; + sortButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(sortButton); + + class MyOwnAction : public ui::ButtonAction + { + SearchView * v; + public: + MyOwnAction(SearchView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->ShowOwn(sender->GetToggleState()); + } + }; + ownButton = new ui::Button(ui::Point(XRES+BARSIZE-70, 10), ui::Point(61, 17), "My Own"); + ownButton->SetIcon(IconMyOwn); + ownButton->SetTogglable(true); + ownButton->SetActionCallback(new MyOwnAction(this)); + ownButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; + ownButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(ownButton); + + class FavAction : public ui::ButtonAction + { + SearchView * v; + public: + FavAction(SearchView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->ShowFavourite(sender->GetToggleState()); + } + }; + favButton = new ui::Button(searchField->Position+ui::Point(searchField->Size.X+15, 0), ui::Point(17, 17), ""); + favButton->SetIcon(IconFavourite); + favButton->SetTogglable(true); + favButton->Appearance.Margin.Left+=2; + favButton->SetActionCallback(new FavAction(this)); + favButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; + favButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + favButton->Appearance.BorderInactive = ui::Colour(170,170,170); + AddComponent(favButton); + + class ClearSearchAction : public ui::ButtonAction + { + SearchView * v; + public: + ClearSearchAction(SearchView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->clearSearch(); + } + }; + ui::Button * clearSearchButton = new ui::Button(searchField->Position+ui::Point(searchField->Size.X-1, 0), ui::Point(17, 17), ""); + clearSearchButton->SetIcon(IconClose); + clearSearchButton->SetActionCallback(new ClearSearchAction(this)); + clearSearchButton->Appearance.Margin.Left+=2; + clearSearchButton->Appearance.Margin.Top+=2; + clearSearchButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; + clearSearchButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + clearSearchButton->Appearance.BorderInactive = ui::Colour(170,170,170); + AddComponent(clearSearchButton); + + class NextPageAction : public ui::ButtonAction + { + SearchView * v; + public: + NextPageAction(SearchView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->NextPage(); + } + }; + nextButton->SetActionCallback(new NextPageAction(this)); + nextButton->Appearance.HorizontalAlign = ui::Appearance::AlignRight; + nextButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + class PrevPageAction : public ui::ButtonAction + { + SearchView * v; + public: + PrevPageAction(SearchView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->PrevPage(); + } + }; + previousButton->SetActionCallback(new PrevPageAction(this)); + previousButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + previousButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(nextButton); + AddComponent(previousButton); + AddComponent(searchField); + AddComponent(infoLabel); + + loadingSpinner = new ui::Spinner(ui::Point(((XRES+BARSIZE)/2)-12, ((YRES+MENUSIZE)/2)+12), ui::Point(24, 24)); + AddComponent(loadingSpinner); + + ui::Label * searchPrompt = new ui::Label(ui::Point(10, 10), ui::Point(50, 16), "Search:"); + searchPrompt->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + searchPrompt->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(searchPrompt); + + class RemoveSelectedAction : public ui::ButtonAction + { + SearchView * v; + public: + RemoveSelectedAction(SearchView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->RemoveSelected(); + } + }; + + class UnpublishSelectedAction : public ui::ButtonAction + { + SearchView * v; + public: + UnpublishSelectedAction(SearchView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->UnpublishSelected(); + } + }; + + class FavouriteSelectedAction : public ui::ButtonAction + { + SearchView * v; + public: + FavouriteSelectedAction(SearchView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->FavouriteSelected(); + } + }; + + class ClearSelectionAction : public ui::ButtonAction + { + SearchView * v; + public: + ClearSelectionAction(SearchView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->ClearSelection(); + } + }; + + removeSelected = new ui::Button(ui::Point((((XRES+BARSIZE)-415)/2), YRES+MENUSIZE-18), ui::Point(100, 16), "Delete"); + removeSelected->Visible = false; + removeSelected->SetActionCallback(new RemoveSelectedAction(this)); + AddComponent(removeSelected); + + unpublishSelected = new ui::Button(ui::Point((((XRES+BARSIZE)-415)/2)+105, YRES+MENUSIZE-18), ui::Point(100, 16), "Unpublish"); + unpublishSelected->Visible = false; + unpublishSelected->SetActionCallback(new UnpublishSelectedAction(this)); + AddComponent(unpublishSelected); + + favouriteSelected = new ui::Button(ui::Point((((XRES+BARSIZE)-415)/2)+210, YRES+MENUSIZE-18), ui::Point(100, 16), "Favourite"); + favouriteSelected->Visible = false; + favouriteSelected->SetActionCallback(new FavouriteSelectedAction(this)); + AddComponent(favouriteSelected); + + clearSelection = new ui::Button(ui::Point((((XRES+BARSIZE)-415)/2)+315, YRES+MENUSIZE-18), ui::Point(100, 16), "Clear selection"); + clearSelection->Visible = false; + clearSelection->SetActionCallback(new ClearSelectionAction(this)); + AddComponent(clearSelection); + + CheckAccess(); +} + +void SearchView::NotifyMessageOfTheDay(Client * sender) +{ + motdLabel->SetText(sender->GetMessageOfTheDay()); +} + +void SearchView::doSearch() +{ + if (searchField->GetText().length() > 3 || !searchField->GetText().length()) + c->DoSearch(searchField->GetText()); +} + + +void SearchView::clearSearch() +{ + searchField->SetText(""); + c->DoSearch(searchField->GetText(), true); +} + +void SearchView::OnTryOkay(OkayMethod method) +{ + c->DoSearch(searchField->GetText(), true); +} + +SearchView::~SearchView() +{ + Client::Ref().RemoveListener(this); + RemoveComponent(nextButton); + RemoveComponent(previousButton); + RemoveComponent(infoLabel); + + for(int i = 0; i < saveButtons.size(); i++) + { + RemoveComponent(saveButtons[i]); + delete saveButtons[i]; + } + saveButtons.clear(); + + delete nextButton; + delete previousButton; + delete infoLabel; +} + +void SearchView::Search(std::string query) +{ + searchField->SetText(query); + c->DoSearch(query, true); +} + +void SearchView::NotifySortChanged(SearchModel * sender) +{ + if(sender->GetSort() == "best") + { + sortButton->SetToggleState(false); + sortButton->SetText("By votes"); + sortButton->SetIcon(IconVoteSort); + } + else + { + sortButton->SetToggleState(true); + sortButton->SetText("By date"); + sortButton->SetIcon(IconDateSort); + } +} + +void SearchView::NotifyShowOwnChanged(SearchModel * sender) +{ + ownButton->SetToggleState(sender->GetShowOwn()); + if(sender->GetShowOwn() || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator) + { + unpublishSelected->Enabled = true; + removeSelected->Enabled = true; + } + else if(sender->GetShowFavourite()) + { + unpublishSelected->Enabled = false; + removeSelected->Enabled = false; + } + else + { + unpublishSelected->Enabled = false; + removeSelected->Enabled = false; + } +} + +void SearchView::NotifyShowFavouriteChanged(SearchModel * sender) +{ + favButton->SetToggleState(sender->GetShowFavourite()); + if(sender->GetShowFavourite()) + { + unpublishSelected->Enabled = false; + removeSelected->Enabled = false; + } + else if(sender->GetShowOwn() || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator) + { + unpublishSelected->Enabled = true; + removeSelected->Enabled = true; + } + else + { + unpublishSelected->Enabled = false; + removeSelected->Enabled = false; + } +} + +void SearchView::NotifyPageChanged(SearchModel * sender) +{ + std::stringstream pageInfo; + pageInfo << "Page " << sender->GetPageNum() << " of " << sender->GetPageCount(); + infoLabel->SetText(pageInfo.str()); + if(sender->GetPageNum() == 1) + { + previousButton->Visible = false; + } + else + { + previousButton->Visible = true; + } + if(sender->GetPageNum() == sender->GetPageCount()) + { + nextButton->Visible = false; + } + else + { + nextButton->Visible = true; + } +} + +void SearchView::NotifyAuthUserChanged(Client * sender) +{ + CheckAccess(); +} + +void SearchView::CheckAccess() +{ + if(c) + { + c->ClearSelection(); + + if(ownButton->GetToggleState()) + ownButton->DoAction(); + if(favButton->GetToggleState()) + favButton->DoAction(); + } + + if(Client::Ref().GetAuthUser().ID) + { + ownButton->Enabled = true; + favButton->Enabled = true; + favouriteSelected->Enabled = true; + + if(Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator) + { + unpublishSelected->Enabled = true; + removeSelected->Enabled = true; + for(int i = 0; i < saveButtons.size(); i++) + { + saveButtons[i]->SetSelectable(true); + } + } + + } + else + { + ownButton->Enabled = false; + favButton->Enabled = false; + + + favouriteSelected->Enabled = false; + unpublishSelected->Enabled = false; + removeSelected->Enabled = false; + + for(int i = 0; i < saveButtons.size(); i++) + { + saveButtons[i]->SetSelectable(false); + saveButtons[i]->SetSelected(false); + } + } +} + +void SearchView::NotifyTagListChanged(SearchModel * sender) +{ + int i = 0; + int buttonWidth, buttonHeight, saveX = 0, saveY = 0, savesX = 5, savesY = 4, buttonPadding = 1; + int buttonAreaWidth, buttonAreaHeight, buttonXOffset, buttonYOffset; + + int tagWidth, tagHeight, tagX = 0, tagY = 0, tagsX = 6, tagsY = 4, tagPadding = 1; + int tagAreaWidth, tagAreaHeight, tagXOffset, tagYOffset; + + vector<pair<string, int> > tags = sender->GetTagList(); + + RemoveComponent(motdLabel); + motdLabel->SetParentWindow(NULL); + + RemoveComponent(tagsLabel); + tagsLabel->SetParentWindow(NULL); + + for(i = 0; i < tagButtons.size(); i++) + { + RemoveComponent(tagButtons[i]); + delete tagButtons[i]; + } + tagButtons.clear(); + + buttonYOffset = 28; + buttonXOffset = buttonPadding; + buttonAreaWidth = Size.X; + buttonAreaHeight = Size.Y - buttonYOffset - 18; + + if(sender->GetShowTags()) + { + buttonYOffset += (buttonAreaHeight/savesY) - buttonPadding*2; + buttonAreaHeight = Size.Y - buttonYOffset - 18; + savesY--; + + tagXOffset = tagPadding; + tagYOffset = 60; + tagAreaWidth = Size.X; + tagAreaHeight = ((buttonAreaHeight/savesY) - buttonPadding*2)-(tagYOffset-28)-5; + tagWidth = (tagAreaWidth/tagsX) - tagPadding*2; + tagHeight = (tagAreaHeight/tagsY) - tagPadding*2; + + AddComponent(tagsLabel); + tagsLabel->Position.Y = tagYOffset-16; + + AddComponent(motdLabel); + motdLabel->Position.Y = tagYOffset-30; + } + + class TagAction: public ui::ButtonAction + { + SearchView * v; + std::string tag; + public: + TagAction(SearchView * v, std::string tag) : v(v), tag(tag) {} + virtual void ActionCallback(ui::Button * sender) + { + v->Search(tag); + } + }; + if(sender->GetShowTags()) + { + for(i = 0; i < tags.size(); i++) + { + int maxTagVotes = tags[0].second; + + pair<string, int> tag = tags[i]; + + if(tagX == tagsX) + { + if(tagY == tagsY-1) + break; + tagX = 0; + tagY++; + } + + int tagAlpha = 192; + if (maxTagVotes) + tagAlpha = 127+(128*tag.second)/maxTagVotes; + + ui::Button * tagButton; + tagButton = new ui::Button( + ui::Point( + tagXOffset + tagPadding + tagX*(tagWidth+tagPadding*2), + tagYOffset + tagPadding + tagY*(tagHeight+tagPadding*2) + ), + ui::Point(tagWidth, tagHeight), + tag.first + ); + tagButton->SetActionCallback(new TagAction(this, tag.first)); + tagButton->Appearance.BorderInactive = ui::Colour(0, 0, 0); + tagButton->Appearance.BorderHover = ui::Colour(0, 0, 0); + tagButton->Appearance.BorderActive = ui::Colour(0, 0, 0); + tagButton->Appearance.BackgroundHover = ui::Colour(0, 0, 0); + + tagButton->Appearance.TextInactive = ui::Colour(tagAlpha, tagAlpha, tagAlpha); + tagButton->Appearance.TextHover = ui::Colour((tagAlpha*5)/6, (tagAlpha*5)/6, tagAlpha); + AddComponent(tagButton); + tagButtons.push_back(tagButton); + tagX++; + + } + } +} + +void SearchView::NotifySaveListChanged(SearchModel * sender) +{ + int i = 0; + int buttonWidth, buttonHeight, saveX = 0, saveY = 0, savesX = 5, savesY = 4, buttonPadding = 1; + int buttonAreaWidth, buttonAreaHeight, buttonXOffset, buttonYOffset; + + int tagWidth, tagHeight, tagX = 0, tagY = 0, tagsX = 6, tagsY = 4, tagPadding = 1; + int tagAreaWidth, tagAreaHeight, tagXOffset, tagYOffset; + + vector<SaveInfo*> saves = sender->GetSaveList(); + //string messageOfTheDay = sender->GetMessageOfTheDay(); + + if(sender->GetShowFavourite()) + favouriteSelected->SetText("Unfavourite"); + else + favouriteSelected->SetText("Favourite"); + + Client::Ref().ClearThumbnailRequests(); + for(i = 0; i < saveButtons.size(); i++) + { + RemoveComponent(saveButtons[i]); + } + if(!sender->GetSavesLoaded()) + { + nextButton->Enabled = false; + previousButton->Enabled = false; + favButton->Enabled = false; + } + else + { + nextButton->Enabled = true; + previousButton->Enabled = true; + if (Client::Ref().GetAuthUser().ID) + favButton->Enabled = true; + } + if (!sender->GetSavesLoaded() || favButton->GetToggleState()) + { + ownButton->Enabled = false; + sortButton->Enabled = false; + } + else + { + if (Client::Ref().GetAuthUser().ID) + ownButton->Enabled = true; + sortButton->Enabled = true; + } + if(!saves.size()) + { + loadingSpinner->Visible = false; + if(!errorLabel) + { + errorLabel = new ui::Label(ui::Point(((XRES+BARSIZE)/2)-100, ((YRES+MENUSIZE)/2)-6), ui::Point(200, 12), "Error"); + AddComponent(errorLabel); + } + if(!sender->GetSavesLoaded()) + { + errorLabel->SetText("Loading..."); + loadingSpinner->Visible = true; + } + else + { + if(sender->GetLastError().length()) + errorLabel->SetText("\bo" + sender->GetLastError()); + else + errorLabel->SetText("\boNo saves found"); + } + } + else + { + loadingSpinner->Visible = false; + if(errorLabel) + { + RemoveComponent(errorLabel); + delete errorLabel; + errorLabel = NULL; + } + for(i = 0; i < saveButtons.size(); i++) + { + delete saveButtons[i]; + } + saveButtons.clear(); + + buttonYOffset = 28; + buttonXOffset = buttonPadding; + buttonAreaWidth = Size.X; + buttonAreaHeight = Size.Y - buttonYOffset - 18; + + if(sender->GetShowTags()) + { + buttonYOffset += (buttonAreaHeight/savesY) - buttonPadding*2; + buttonAreaHeight = Size.Y - buttonYOffset - 18; + savesY--; + + tagXOffset = tagPadding; + tagYOffset = 60; + tagAreaWidth = Size.X; + tagAreaHeight = ((buttonAreaHeight/savesY) - buttonPadding*2)-(tagYOffset-28)-5; + tagWidth = (tagAreaWidth/tagsX) - tagPadding*2; + tagHeight = (tagAreaHeight/tagsY) - tagPadding*2; + } + + buttonWidth = (buttonAreaWidth/savesX) - buttonPadding*2; + buttonHeight = (buttonAreaHeight/savesY) - buttonPadding*2; + + + + class SaveOpenAction: public ui::SaveButtonAction + { + SearchView * v; + public: + SaveOpenAction(SearchView * _v) { v = _v; } + virtual void ActionCallback(ui::SaveButton * sender) + { + v->c->OpenSave(sender->GetSave()->GetID(), sender->GetSave()->GetVersion()); + } + virtual void SelectedCallback(ui::SaveButton * sender) + { + v->c->Selected(sender->GetSave()->GetID(), sender->GetSelected()); + } + virtual void AltActionCallback(ui::SaveButton * sender) + { + stringstream search; + search << "history:" << sender->GetSave()->GetID(); + v->Search(search.str()); + } + virtual void AltActionCallback2(ui::SaveButton * sender) + { + v->Search("user:"+sender->GetSave()->GetUserName()); + } + }; + for(i = 0; i < saves.size(); i++) + { + if(saveX == savesX) + { + if(saveY == savesY-1) + break; + saveX = 0; + saveY++; + } + ui::SaveButton * saveButton; + saveButton = new ui::SaveButton( + ui::Point( + buttonXOffset + buttonPadding + saveX*(buttonWidth+buttonPadding*2), + buttonYOffset + buttonPadding + saveY*(buttonHeight+buttonPadding*2) + ), + ui::Point(buttonWidth, buttonHeight), + saves[i]); + saveButton->AddContextMenu(0); + saveButton->SetActionCallback(new SaveOpenAction(this)); + if(Client::Ref().GetAuthUser().ID) + saveButton->SetSelectable(true); + if (saves[i]->GetUserName() == Client::Ref().GetAuthUser().Username || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator) + saveButton->SetShowVotes(true); + saveButtons.push_back(saveButton); + AddComponent(saveButton); + saveX++; + } + } +} + +void SearchView::NotifySelectedChanged(SearchModel * sender) +{ + vector<int> selected = sender->GetSelected(); + for(int j = 0; j < saveButtons.size(); j++) + { + saveButtons[j]->SetSelected(false); + for(int i = 0; i < selected.size(); i++) + { + if(saveButtons[j]->GetSave()->GetID()==selected[i]) + saveButtons[j]->SetSelected(true); + } + } + + if(selected.size()) + { + removeSelected->Visible = true; + unpublishSelected->Visible = true; + favouriteSelected->Visible = true; + clearSelection->Visible = true; + } + else + { + removeSelected->Visible = false; + unpublishSelected->Visible = false; + favouriteSelected->Visible = false; + clearSelection->Visible = false; + } +} + +void SearchView::OnTick(float dt) +{ + c->Update(); +} + +void SearchView::OnMouseWheel(int x, int y, int d) +{ + if(!d) + return; + if(d<0) + c->NextPage(); + else + c->PrevPage(); +} +void SearchView::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + if(key==KEY_ESCAPE) + c->Exit(); +} + diff --git a/src/gui/search/SearchView.h b/src/gui/search/SearchView.h new file mode 100644 index 0000000..5752a2c --- /dev/null +++ b/src/gui/search/SearchView.h @@ -0,0 +1,74 @@ +#ifndef SEARCHVIEW_H +#define SEARCHVIEW_H + +#include <vector> +#include "SearchController.h" +#include "gui/interface/SaveButton.h" +#include "gui/interface/Button.h" +#include "gui/interface/Label.h" +#include "gui/interface/Spinner.h" +#include "gui/interface/Textbox.h" +#include "client/ClientListener.h" + +using namespace std; + +namespace ui +{ + class RichLabel; + class SaveButton; + class Button; + class Label; + class Spinner; + class Textbox; +} + +class SearchModel; +class SearchController; + +class SearchView: public ui::Window, public ClientListener +{ +private: + SearchController * c; + vector<ui::SaveButton*> saveButtons; + vector<ui::Button*> tagButtons; + ui::Button * favButton; + ui::Button * nextButton; + ui::Button * previousButton; + ui::Label * errorLabel; + ui::Textbox * searchField; + ui::Label * infoLabel; + ui::Label * tagsLabel; + ui::RichLabel * motdLabel; + ui::Button * sortButton; + ui::Button * ownButton; + ui::Spinner * loadingSpinner; + + ui::Button * removeSelected; + ui::Button * unpublishSelected; + ui::Button * favouriteSelected; + ui::Button * clearSelection; + void clearSearch(); + void doSearch(); +public: + void NotifyTagListChanged(SearchModel * sender); + void NotifySaveListChanged(SearchModel * sender); + void NotifySelectedChanged(SearchModel * sender); + void NotifyPageChanged(SearchModel * sender); + void NotifySortChanged(SearchModel * sender); + void NotifyShowOwnChanged(SearchModel * sender); + void NotifyShowFavouriteChanged(SearchModel * sender); + void NotifyAuthUserChanged(Client * sender); + void NotifyMessageOfTheDay(Client * sender); + void CheckAccess(); + virtual void OnTryOkay(OkayMethod method); + SearchView(); + virtual ~SearchView(); + void AttachController(SearchController * _c) { c = _c; } + virtual void Search(std::string); + virtual void OnTick(float dt); + virtual void OnMouseWheel(int x, int y, int d); + virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + +}; + +#endif // SEARCHVIEW_H diff --git a/src/gui/search/Thumbnail.cpp b/src/gui/search/Thumbnail.cpp new file mode 100644 index 0000000..1f06c45 --- /dev/null +++ b/src/gui/search/Thumbnail.cpp @@ -0,0 +1,73 @@ +#include "Thumbnail.h" + +Thumbnail::Thumbnail(const Thumbnail & thumb): + ID(thumb.ID), + Datestamp(thumb.Datestamp), + Data(thumb.Data), + Size(thumb.Size) +{ + //Ensure the actual thumbnail data is copied + if(thumb.Data) + { + Data = new pixel[thumb.Size.X*thumb.Size.Y]; + memcpy(Data, thumb.Data, (thumb.Size.X*thumb.Size.Y) * PIXELSIZE); + } + else + { + Data = NULL; + } +} + +Thumbnail::Thumbnail(int _id, int _datestamp, pixel * _data, ui::Point _size): + ID(_id), + Datestamp(_datestamp), + Data(_data), + Size(_size) +{ + if(_data) + { + Data = new pixel[_size.X*_size.Y]; + memcpy(Data, _data, (_size.X*_size.Y) * PIXELSIZE); + } + else + { + Data = NULL; + } +} + +void Thumbnail::Resize(int width, int height) +{ + Resize(ui::Point(width, height)); +} + +void Thumbnail::Resize(ui::Point newSize) +{ + float scaleFactorX = 1.0f, scaleFactorY = 1.0f; + if(Size.Y > newSize.Y) + { + scaleFactorY = float(newSize.Y)/((float)Size.Y); + } + if(Size.X > newSize.X) + { + scaleFactorX = float(newSize.X)/((float)Size.X); + } + if(newSize.X == -1) + scaleFactorX = scaleFactorY; + if(newSize.Y == -1) + scaleFactorY = scaleFactorX; + if(scaleFactorY < 1.0f || scaleFactorX < 1.0f) + { + float scaleFactor = scaleFactorY < scaleFactorX ? scaleFactorY : scaleFactorX; + pixel * thumbData = Data; + Data = Graphics::resample_img(thumbData, Size.X, Size.Y, Size.X * scaleFactor, Size.Y * scaleFactor); + Size.X *= scaleFactor; + Size.Y *= scaleFactor; + delete[] thumbData; + } +} + +Thumbnail::~Thumbnail() +{ + if(Data) + delete[] Data; +} diff --git a/src/gui/search/Thumbnail.h b/src/gui/search/Thumbnail.h new file mode 100644 index 0000000..27ffa87 --- /dev/null +++ b/src/gui/search/Thumbnail.h @@ -0,0 +1,25 @@ +#ifndef THUMBNAIL_H +#define THUMBNAIL_H + +#include <iostream> +#include "graphics/Graphics.h" +#include "gui/interface/Point.h" + +class Thumbnail +{ +public: + Thumbnail(const Thumbnail & thumb); + + Thumbnail(int _id, int _datestamp, pixel * _data, ui::Point _size); + + ~Thumbnail(); + + void Resize(int Width, int Height); + void Resize(ui::Point newSize); + + int ID, Datestamp; + ui::Point Size; + pixel * Data; +}; + +#endif // THUMBNAIL_H diff --git a/src/gui/tags/TagsController.cpp b/src/gui/tags/TagsController.cpp new file mode 100644 index 0000000..4165642 --- /dev/null +++ b/src/gui/tags/TagsController.cpp @@ -0,0 +1,53 @@ +#include "TagsController.h" +#include "gui/interface/Engine.h" + +#include "TagsModel.h" +#include "TagsView.h" + +TagsController::TagsController(ControllerCallback * callback, SaveInfo * save): + HasDone(false) +{ + tagsModel = new TagsModel(); + tagsView = new TagsView(); + tagsView->AttachController(this); + tagsModel->AddObserver(tagsView); + + tagsModel->SetSave(save); + + this->callback = callback; +} + +SaveInfo * TagsController::GetSave() +{ + return tagsModel->GetSave(); +} + +void TagsController::RemoveTag(std::string tag) +{ + tagsModel->RemoveTag(tag); +} + + +void TagsController::AddTag(std::string tag) +{ + tagsModel->AddTag(tag); +} + +void TagsController::Exit() +{ + if(ui::Engine::Ref().GetWindow() == tagsView) + ui::Engine::Ref().CloseWindow(); + if(callback) + callback->ControllerExit(); + HasDone = true; +} + +TagsController::~TagsController() { + if(ui::Engine::Ref().GetWindow() == tagsView) + ui::Engine::Ref().CloseWindow(); + delete tagsModel; + delete tagsView; + if(callback) + delete callback; +} + diff --git a/src/gui/tags/TagsController.h b/src/gui/tags/TagsController.h new file mode 100644 index 0000000..8a51098 --- /dev/null +++ b/src/gui/tags/TagsController.h @@ -0,0 +1,25 @@ +#ifndef TAGSCONTROLLER_H_ +#define TAGSCONTROLLER_H_ + +#include "Controller.h" +#include "TagsView.h" +#include "client/SaveInfo.h" + +class TagsView; +class TagsModel; +class TagsController { + ControllerCallback * callback; + TagsView * tagsView; + TagsModel * tagsModel; +public: + bool HasDone; + TagsController(ControllerCallback * callback, SaveInfo * save); + TagsView * GetView() {return tagsView;} + SaveInfo * GetSave(); + void RemoveTag(std::string tag); + void AddTag(std::string tag); + void Exit(); + virtual ~TagsController(); +}; + +#endif /* TAGSCONTROLLER_H_ */ diff --git a/src/gui/tags/TagsModel.cpp b/src/gui/tags/TagsModel.cpp new file mode 100644 index 0000000..f3329ad --- /dev/null +++ b/src/gui/tags/TagsModel.cpp @@ -0,0 +1,76 @@ +#include "TagsModel.h" +#include "TagsView.h" +#include "client/Client.h" +#include "TagsModelException.h" +#include "client/SaveInfo.h" + +TagsModel::TagsModel(): + save(NULL) +{ + +} + +void TagsModel::SetSave(SaveInfo * save) +{ + this->save = save; + notifyTagsChanged(); +} + +SaveInfo * TagsModel::GetSave() +{ + return save; +} + +void TagsModel::RemoveTag(std::string tag) +{ + if(save) + { + std::vector<std::string> * tags = Client::Ref().RemoveTag(save->GetID(), tag); + if(tags) + { + save->SetTags(std::vector<std::string>(*tags)); + notifyTagsChanged(); + delete tags; + } + else + { + throw TagsModelException(Client::Ref().GetLastError()); + } + } +} + +void TagsModel::AddTag(std::string tag) +{ + if(save) + { + std::vector<std::string> * tags = Client::Ref().AddTag(save->GetID(), tag); + if(tags) + { + save->SetTags(std::vector<std::string>(*tags)); + notifyTagsChanged(); + delete tags; + } + else + { + throw TagsModelException(Client::Ref().GetLastError()); + } + } +} + +void TagsModel::AddObserver(TagsView * observer) +{ + observers.push_back(observer); + observer->NotifyTagsChanged(this); +} + +void TagsModel::notifyTagsChanged() +{ + for(int i = 0; i < observers.size(); i++) + { + observers[i]->NotifyTagsChanged(this); + } +} + +TagsModel::~TagsModel() { +} + diff --git a/src/gui/tags/TagsModel.h b/src/gui/tags/TagsModel.h new file mode 100644 index 0000000..8afe841 --- /dev/null +++ b/src/gui/tags/TagsModel.h @@ -0,0 +1,24 @@ +#ifndef TAGSMODEL_H_ +#define TAGSMODEL_H_ + +#include <vector> +#include <string> + +class SaveInfo; + +class TagsView; +class TagsModel { + SaveInfo * save; + std::vector<TagsView*> observers; + void notifyTagsChanged(); +public: + TagsModel(); + void AddObserver(TagsView * observer); + void SetSave(SaveInfo * save); + void RemoveTag(std::string tag); + void AddTag(std::string tag); + SaveInfo * GetSave(); + virtual ~TagsModel(); +}; + +#endif /* TAGSMODEL_H_ */ diff --git a/src/gui/tags/TagsModelException.h b/src/gui/tags/TagsModelException.h new file mode 100644 index 0000000..671bf3b --- /dev/null +++ b/src/gui/tags/TagsModelException.h @@ -0,0 +1,15 @@ +#ifndef TAGSMODELEXCEPTION_H_ +#define TAGSMODELEXCEPTION_H_ + +#include <string> +#include <exception> + +class TagsModelException { + std::string message; +public: + TagsModelException(std::string message_): message(message_) {}; + const char * what() const throw() { return message.c_str(); }; + ~TagsModelException() throw() {}; +}; + +#endif /* TAGSMODELEXCEPTION_H_ */ diff --git a/src/gui/tags/TagsView.cpp b/src/gui/tags/TagsView.cpp new file mode 100644 index 0000000..03b2893 --- /dev/null +++ b/src/gui/tags/TagsView.cpp @@ -0,0 +1,159 @@ +#include "client/Client.h" +#include "TagsView.h" + +#include "gui/dialogues/ErrorMessage.h" +#include "TagsController.h" +#include "TagsModel.h" +#include "TagsModelException.h" + +#include "gui/interface/Button.h" +#include "gui/interface/Textbox.h" +#include "gui/interface/Label.h" +#include "gui/interface/Keys.h" + +TagsView::TagsView(): + ui::Window(ui::Point(-1, -1), ui::Point(195, 250)) +{ + + class CloseAction : public ui::ButtonAction + { + TagsView * v; + public: + CloseAction(TagsView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->c->Exit(); + } + }; + closeButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(195, 16), "Close"); + closeButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + closeButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + closeButton->SetActionCallback(new CloseAction(this)); + AddComponent(closeButton); + + + tagInput = new ui::Textbox(ui::Point(8, Size.Y-40), ui::Point(Size.X-60, 16), "", "[new tag]"); + tagInput->Appearance.icon = IconTag; + tagInput->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + tagInput->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + AddComponent(tagInput); + + class AddTagAction : public ui::ButtonAction + { + TagsView * v; + public: + AddTagAction(TagsView * _v) { v = _v; } + void ActionCallback(ui::Button * sender) + { + v->addTag(); + } + }; + addButton = new ui::Button(ui::Point(tagInput->Position.X+tagInput->Size.X+4, tagInput->Position.Y), ui::Point(40, 16), "Add"); + addButton->Appearance.icon = IconAdd; + addButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + addButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + addButton->SetActionCallback(new AddTagAction(this)); + AddComponent(addButton); + + title = new ui::Label(ui::Point(5, 5), ui::Point(185, 16), "Manage tags:"); + title->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + title->Appearance.VerticalAlign = ui::Appearance::AlignTop; + AddComponent(title); +} + +void TagsView::OnDraw() +{ + Graphics * g = ui::Engine::Ref().g; + g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); + g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); +} + +void TagsView::NotifyTagsChanged(TagsModel * sender) +{ + for(int i = 0; i < tags.size(); i++) + { + RemoveComponent(tags[i]); + delete tags[i]; + } + tags.clear(); + + + class DeleteTagAction : public ui::ButtonAction + { + TagsView * v; + std::string tag; + public: + DeleteTagAction(TagsView * _v, std::string tag) { v = _v; this->tag = tag; } + void ActionCallback(ui::Button * sender) + { + try + { + v->c->RemoveTag(tag); + } + catch(TagsModelException & ex) + { + new ErrorMessage("Could not remove tag", ex.what()); + } + } + }; + + if(sender->GetSave()) + { + for(int i = 0; i < sender->GetSave()->GetTags().size(); i++) + { + ui::Label * tempLabel = new ui::Label(ui::Point(35, 35+(16*i)), ui::Point(120, 16), sender->GetSave()->GetTags()[i]); + tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + tags.push_back(tempLabel); + AddComponent(tempLabel); + + if(sender->GetSave()->GetUserName() == Client::Ref().GetAuthUser().Username || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator) + { + ui::Button * tempButton = new ui::Button(ui::Point(15, 37+(16*i)), ui::Point(11, 12)); + tempButton->Appearance.icon = IconDelete; + tempButton->Appearance.Border = ui::Border(0); + tempButton->Appearance.Margin.Top += 2; + tempButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; + tempButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + tempButton->SetActionCallback(new DeleteTagAction(this, sender->GetSave()->GetTags()[i])); + tags.push_back(tempButton); + AddComponent(tempButton); + } + } + } +} + +void TagsView::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) +{ + switch(key) + { + case KEY_ENTER: + case KEY_RETURN: + if(IsFocused(tagInput)) + { + addTag(); + } + break; + } +} + +void TagsView::addTag() +{ + if (tagInput->GetText().length() < 4) + { + new ErrorMessage("Tag not long enough", "Must be at least 4 letters"); + return; + } + try + { + c->AddTag(tagInput->GetText()); + } + catch(TagsModelException & ex) + { + new ErrorMessage("Could not add tag", ex.what()); + } + tagInput->SetText(""); +} + +TagsView::~TagsView() { +} + diff --git a/src/gui/tags/TagsView.h b/src/gui/tags/TagsView.h new file mode 100644 index 0000000..a842e13 --- /dev/null +++ b/src/gui/tags/TagsView.h @@ -0,0 +1,33 @@ +#ifndef TAGSVIEW_H_ +#define TAGSVIEW_H_ + +#include <vector> +#include "gui/interface/Window.h" + +namespace ui +{ + class Button; + class Textbox; + class Label; +} + +class TagsController; +class TagsModel; +class TagsView: public ui::Window { + TagsController * c; + ui::Button * addButton; + ui::Button * closeButton; + ui::Label * title; + ui::Textbox * tagInput; + std::vector<ui::Component*> tags; + void addTag(); +public: + TagsView(); + virtual void OnDraw(); + void AttachController(TagsController * c_) { c = c_; }; + virtual void OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt); + void NotifyTagsChanged(TagsModel * sender); + virtual ~TagsView(); +}; + +#endif /* TAGSVIEW_H_ */ diff --git a/src/gui/update/UpdateActivity.cpp b/src/gui/update/UpdateActivity.cpp new file mode 100644 index 0000000..7eadf7f --- /dev/null +++ b/src/gui/update/UpdateActivity.cpp @@ -0,0 +1,164 @@ +#include <bzlib.h> +#include <sstream> +#include "gui/dialogues/ConfirmPrompt.h" +#include "gui/interface/Engine.h" +#include "UpdateActivity.h" +#include "tasks/Task.h" +#include "client/HTTP.h" +#include "client/Client.h" +#include "Update.h" +#include "Misc.h" + + +class UpdateDownloadTask : public Task +{ +public: + UpdateDownloadTask(std::string updateName, UpdateActivity * a) : updateName(updateName), a(a) {}; +private: + UpdateActivity * a; + std::string updateName; + virtual void notifyDoneMain(){ + a->NotifyDone(this); + } + virtual void notifyErrorMain() + { + a->NotifyError(this); + } + virtual bool doWork() + { + std::stringstream errorStream; + void * request = http_async_req_start(NULL, (char*)updateName.c_str(), NULL, 0, 0); + notifyStatus("Downloading update"); + notifyProgress(-1); + while(!http_async_req_status(request)) + { + int total, done; + http_async_get_length(request, &total, &done); + notifyProgress((float(done)/float(total))*100.0f); + } + + char * data; + int dataLength, status; + data = http_async_req_stop(request, &status, &dataLength); + if (status!=200) + { + if (data) + free(data); + errorStream << "Server responded with Status " << status; + notifyError("Could not download update"); + return false; + } + if (!data) + { + errorStream << "Server responded with nothing"; + notifyError("Server did not return any data"); + return false; + } + + notifyStatus("Unpacking update"); + notifyProgress(-1); + + int uncompressedLength; + + if(dataLength<16) + { + errorStream << "Unsufficient data, got " << dataLength << " bytes"; + goto corrupt; + } + if (data[0]!=0x42 || data[1]!=0x75 || data[2]!=0x54 || data[3]!=0x54) + { + errorStream << "Invalid update format"; + goto corrupt; + } + + uncompressedLength = (unsigned char)data[4]; + uncompressedLength |= ((unsigned char)data[5])<<8; + uncompressedLength |= ((unsigned char)data[6])<<16; + uncompressedLength |= ((unsigned char)data[7])<<24; + + char * res; + res = (char *)malloc(uncompressedLength); + if (!res) + { + errorStream << "Unable to allocate " << uncompressedLength << " bytes of memory for decompression"; + goto corrupt; + } + + int dstate; + dstate = BZ2_bzBuffToBuffDecompress((char *)res, (unsigned *)&uncompressedLength, (char *)(data+8), dataLength-8, 0, 0); + if (dstate) + { + errorStream << "Unable to decompress update: " << dstate; + free(res); + goto corrupt; + } + + free(data); + + notifyStatus("Applying update"); + notifyProgress(-1); + + Client::Ref().SetPref("version.update", true); + Client::Ref().WritePrefs(); + if (update_start(res, uncompressedLength)) + { + Client::Ref().SetPref("version.update", false); + update_cleanup(); + notifyError("Update failed - try downloading a new version."); + return false; + } + + return true; + + corrupt: + notifyError("Downloaded update is corrupted\n" + errorStream.str()); + free(data); + return false; + } +}; + +UpdateActivity::UpdateActivity() { + std::stringstream file; + file << "http://" << SERVER << Client::Ref().GetUpdateInfo().File; + updateDownloadTask = new UpdateDownloadTask(file.str(), this); + updateWindow = new TaskWindow("Downloading update...", updateDownloadTask, true); +} + +void UpdateActivity::NotifyDone(Task * sender) +{ + if(sender->GetSuccess()) + { + Exit(); + } +} + +void UpdateActivity::Exit() +{ + updateWindow->Exit(); + ui::Engine::Ref().Exit(); + delete this; +} + +void UpdateActivity::NotifyError(Task * sender) +{ + class ErrorMessageCallback: public ConfirmDialogueCallback + { + UpdateActivity * a; + public: + ErrorMessageCallback(UpdateActivity * a_) { a = a_; } + virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) { + if (result == ConfirmPrompt::ResultOkay) + { + OpenURI("http://powdertoy.co.uk/Download.html"); + } + a->Exit(); + } + virtual ~ErrorMessageCallback() { } + }; + new ConfirmPrompt("Autoupdate failed", "Please visit the website to download a newer version.\nError: " + sender->GetError(), new ErrorMessageCallback(this)); +} + + +UpdateActivity::~UpdateActivity() { +} + diff --git a/src/gui/update/UpdateActivity.h b/src/gui/update/UpdateActivity.h new file mode 100644 index 0000000..6ba5fb3 --- /dev/null +++ b/src/gui/update/UpdateActivity.h @@ -0,0 +1,17 @@ +#ifndef UPDATEACTIVITY_H_ +#define UPDATEACTIVITY_H_ + +#include "tasks/TaskWindow.h" + +class UpdateActivity { + Task * updateDownloadTask; + TaskWindow * updateWindow; +public: + UpdateActivity(); + virtual ~UpdateActivity(); + void Exit(); + virtual void NotifyDone(Task * sender); + virtual void NotifyError(Task * sender); +}; + +#endif /* UPDATEACTIVITY_H_ */ |
