//////////////////////////////////////////////////////////////////////////////////////// // Kara Jensen - mail@karajensen.com - GUIForm.h //////////////////////////////////////////////////////////////////////////////////////// #pragma once #include <vcclr.h> #include "GUICallbacks.h" namespace GUI { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; /** * Main GUI Window Form, usable with the designer */ public ref class GUIForm : public System::Windows::Forms::Form { public: /** * Constructor */ GUIForm(void) : m_callbacks(nullptr), m_toolSelected(nullptr), m_pinnedSimForm(nullptr) { InitializeComponent(); CreateSimulationForm(); CreateButtons(); } /** * Determines and returns the window/instance handles of the gui * @return The handles to the simulation window/instance */ WindowHandle GetWindowHandle() { Form^ simForm = (Form^)*m_pinnedSimForm; WindowHandle window; IntPtr handle = simForm->Handle; IntPtr instance = System::Runtime::InteropServices::Marshal::GetHINSTANCE( System::Reflection::Assembly::GetExecutingAssembly()->GetModules()[0]); window.handle = static_cast<HWND>(handle.ToPointer()); window.instance = static_cast<HINSTANCE>(instance.ToPointer()); return window; } /** * Recieves the native callbacks and fills in the spinbox values * @param callbacks The callbacks from the native application */ void SetCallbacks(GuiCallbacks* callbacks) { m_callbacks = callbacks; m_spacing->numeric->Value = Decimal(callbacks->getSpacing()); m_iterations->numeric->Value = Decimal(callbacks->getIterations()); m_timestep->numeric->Value = Decimal(callbacks->getTimestep()); m_vertRows->numeric->Value = Decimal(callbacks->getVertexRows()); } /** * Enables/Disables the gui controls conditional on scene creation * @param enable Whether to enable/disable mesh creation */ void EnableMeshCreation(bool enable) { m_cynlinderBtn->BackColor = enable ? m_unpressedColor : m_pressedColor; m_sphereBtn->BackColor = enable ? m_unpressedColor : m_pressedColor; m_boxBtn->BackColor = enable ? m_unpressedColor : m_pressedColor; m_cynlinderBtn->Enabled = enable; m_sphereBtn->Enabled = enable; m_boxBtn->Enabled = enable; } protected: /** * Finaliser: Clean up any resources being used */ !GUIForm() { #ifdef _DEBUG OutputDebugString(L"GUIForm::!GUIForm\n"); #endif if (components) { delete components; components = nullptr; } if (m_pinnedSimForm) { //free gcroot handle gcroot<Form^>* ptr = reinterpret_cast<gcroot<Form^>*>(m_pinnedSimForm); m_pinnedSimForm = nullptr; delete ptr; } } /** * Destructor: Will automatically suppress finalisation if called */ ~GUIForm() { this->!GUIForm(); } /** * Overrides the form message handler. This prevents the * window from redrawing when the alt key is pressed * @param m The windows message to handle */ virtual void WndProc(System::Windows::Forms::Message% m) override { if (m.Msg != WM_UPDATEUISTATE) { System::Windows::Forms::Form::WndProc(m); } } private: /** * Reference structure for the gui spinbox */ ref struct SpinBox { Panel^ panel; ///< Panel to encase the spinbox NumericUpDown^ numeric; ///< Spin box control }; /** * Creates the main simulation form */ void CreateSimulationForm() { Form^ simulatorForm = gcnew Form(); simulatorForm->TopLevel = false; simulatorForm->FormBorderStyle = System::Windows::Forms::FormBorderStyle::None; simulatorForm->Dock = System::Windows::Forms::DockStyle::Fill; simulatorForm->Visible = true; simulatorForm->Size = System::Drawing::Size(800, 600); simulatorForm->Location = System::Drawing::Point(0, 0); simulatorForm->BackColor = System::Drawing::Color::FromArgb(190, 190, 195); m_pinnedSimForm = new gcroot<Form^>(simulatorForm); m_mainPanel->Controls->Add((Form^)*m_pinnedSimForm); } /** * Creates and adds a checkbox to the gui * @param checkbox A tracker reference to the checkbox control * @param image The image filename for the checkbox icon * @param tip A tooltip description for the checkbox * @param index The index for the checkbox, used to determine positioning * @param callback The method to call when the checkbox value changes */ void CreateCheckBox(CheckBox^% checkbox, String^ image, String^ tip, int index, EventHandler^ callback) { checkbox = gcnew CheckBox(); checkbox->Appearance = System::Windows::Forms::Appearance::Button; checkbox->CheckStateChanged += callback; CreateControl(checkbox, image, tip, index); } /** * Creates and adds a radio button to the gui * @param button A tracker reference to the radiobutton control * @param image The image filename for the radiobutton icon * @param tip A tooltip description for the radiobutton * @param index The index for the radiobutton, used to determine positioning * @param callback The method to call when the radiobutton value changes */ void CreateRadioButton(RadioButton^% button, String^ image, String^ tip, int index, EventHandler^ callback) { button = gcnew RadioButton(); button->Appearance = System::Windows::Forms::Appearance::Button; button->Click += callback; CreateControl(button, image, tip, index); } /** * Creates and adds a button to the gui * @param button A tracker reference to the button control * @param image The image filename for the button icon * @param tip A tooltip description for the button * @param index The index for the button, used to determine positioning * @param callback The method to call when the button is clicked */ void CreateButton(Button^% button, String^ image, String^ tip, int index, EventHandler^ callback) { button = gcnew Button(); button->Click += callback; CreateControl(button, image, tip, index); } /** * On GUI Close Event */ System::Void GUIFormClosed(System::Object^ sender, FormClosedEventArgs^ e) { m_callbacks->quitFn(); } /** * On Gravity Check Change */ System::Void GravityCheckedChanged(System::Object^ sender, System::EventArgs^ e) { CheckBox^ checkbox = static_cast<CheckBox^>(sender); m_callbacks->setGravity(checkbox->Checked); } /** * On Gravity Check Change */ System::Void WireCheckedChanged(System::Object^ sender, System::EventArgs^ e) { CheckBox^ checkbox = static_cast<CheckBox^>(sender); m_callbacks->setWireframeMode(checkbox->Checked); } /** * On Handle Mode Check Change */ System::Void HandleCheckedChanged(System::Object^ sender, System::EventArgs^ e) { CheckBox^ checkbox = static_cast<CheckBox^>(sender); m_callbacks->setHandleMode(checkbox->Checked); m_gravityBtn->Enabled = !checkbox->Checked; m_gravityBtn->BackgroundImage = checkbox->Checked ? m_disabledGravity : m_enabledGravity; if(checkbox->Checked) { //explicitly use gravity callback to prevent small jump of cloth m_gravityBtn->Checked = false; m_callbacks->setGravity(false); } } /** * On Vertex Visibility Check Change */ System::Void VertsCheckedChanged(System::Object^ sender, System::EventArgs^ e) { CheckBox^ checkbox = static_cast<CheckBox^>(sender); m_callbacks->setVertsVisible(checkbox->Checked); } /** * On Animate Button Press */ System::Void AnimateClick(System::Object^ sender, System::EventArgs^ e) { RadioButton^ button = static_cast<RadioButton^>(sender); SelectRadioButton(button); m_callbacks->setAnimateTool(); } /** * On Move Button Press */ System::Void MoveClick(System::Object^ sender, System::EventArgs^ e) { RadioButton^ button = static_cast<RadioButton^>(sender); SelectRadioButton(button); m_callbacks->setMoveTool(); } /** * On Rotate Button Press */ System::Void RotateClick(System::Object^ sender, System::EventArgs^ e) { RadioButton^ button = static_cast<RadioButton^>(sender); SelectRadioButton(button); m_callbacks->setRotateTool(); } /** * On Scale Button Press */ System::Void ScaleClick(System::Object^ sender, System::EventArgs^ e) { RadioButton^ button = static_cast<RadioButton^>(sender); SelectRadioButton(button); m_callbacks->setScaleTool(); } /** * On Reset Cloth Button Press */ System::Void ResetClothClick(System::Object^ sender, System::EventArgs^ e) { m_callbacks->resetCloth(); } /** * On Reset Camera Button Press */ System::Void ResetCamClick(System::Object^ sender, System::EventArgs^ e) { m_callbacks->resetCamera(); } /** * On Unpin Vertex Button Press */ System::Void UnpinClick(System::Object^ sender, System::EventArgs^ e) { m_callbacks->unpinCloth(); } /** * On Create Box Button Press */ System::Void BoxClick(System::Object^ sender, System::EventArgs^ e) { m_callbacks->createBox(); } /** * On Create Sphere Button Press */ System::Void SphereClick(System::Object^ sender, System::EventArgs^ e) { m_callbacks->createSphere(); } /** * On Create Cylinder Button Press */ System::Void CynlinderClick(System::Object^ sender, System::EventArgs^ e) { m_callbacks->createCylinder(); } /** * On Clear Scene Button Press */ System::Void RemoveClick(System::Object^ sender, System::EventArgs^ e) { m_callbacks->clearScene(); } /** * On Vertex Row Number Value Changed */ System::Void VertexRowsChanged(System::Object^ sender, System::EventArgs^ e) { NumericUpDown^ spinbox = static_cast<NumericUpDown^>(sender); m_handleBtn->Checked = false; m_callbacks->setHandleMode(false); m_callbacks->setVertexRows(Decimal::ToDouble(spinbox->Value)); } /** * On Iteration Value Changed */ System::Void IterationChanged(System::Object^ sender, System::EventArgs^ e) { NumericUpDown^ spinbox = static_cast<NumericUpDown^>(sender); m_callbacks->setIterations(Decimal::ToDouble(spinbox->Value)); } /** * On Timestep Value Changed */ System::Void TimestepChanged(System::Object^ sender, System::EventArgs^ e) { NumericUpDown^ spinbox = static_cast<NumericUpDown^>(sender); m_callbacks->setTimestep(Decimal::ToDouble(spinbox->Value)); } /** * On Cloth Spacing Value Changed */ System::Void SpacingChanged(System::Object^ sender, System::EventArgs^ e) { NumericUpDown^ spinbox = static_cast<NumericUpDown^>(sender); m_handleBtn->Checked = false; m_callbacks->setHandleMode(false); m_callbacks->setSpacing(Decimal::ToDouble(spinbox->Value)); } /** * Chooses the selected radio button * @param button The button to choose as selected */ void SelectRadioButton(RadioButton^ button) { if(m_toolSelected == button) { button->Checked = false; m_toolSelected = nullptr; } else { m_toolSelected = button; } } /** * Creates the gui buttons */ void CreateButtons() { int index = 0; String^ path = "Resources//Sprites//"; m_pressedColor = Color::DarkGray; m_unpressedColor = Color::FromArgb(230, 230, 230); CreateCheckBox(m_gravityBtn, path+"gravity.png", "Add gravity to the simulation", index++, gcnew System::EventHandler(this, &GUIForm::GravityCheckedChanged)); CreateCheckBox(m_handleBtn, path+"handle.png", "Switch to handle mode", index++, gcnew System::EventHandler(this, &GUIForm::HandleCheckedChanged)); CreateButton(m_unpinBtn, path+"unpin.png", "Unpin all pinned vertices", index++, gcnew System::EventHandler(this, &GUIForm::UnpinClick)); CreateCheckBox(m_vertsBtn, path+"showverts.png", "Toggle vertex visibility", index++, gcnew System::EventHandler(this, &GUIForm::VertsCheckedChanged)); CreateCheckBox(m_wireBtn, path+"wire.png", "Switch to wireframe mode", index++, gcnew System::EventHandler(this, &GUIForm::WireCheckedChanged)); CreateButton(m_resetClothBtn, path+"resetcloth.png", "Reset the cloth", index++, gcnew System::EventHandler(this, &GUIForm::ResetClothClick)); CreateButton(m_resetCamBtn, path+"resetcam.png", "Reset the camera", index++, gcnew System::EventHandler(this, &GUIForm::ResetCamClick)); CreateButton(m_boxBtn, path+"box.png", "Create a box", index++, gcnew System::EventHandler(this, &GUIForm::BoxClick)); CreateButton(m_sphereBtn, path+"sphere.png", "Create a sphere", index++, gcnew System::EventHandler(this, &GUIForm::SphereClick)); CreateButton(m_cynlinderBtn, path+"cylinder.png", "Create a cylinder", index++, gcnew System::EventHandler(this, &GUIForm::CynlinderClick)); CreateRadioButton(m_animateBtn, path+"animate.png", "Animate selected object", index++, gcnew System::EventHandler(this, &GUIForm::AnimateClick)); CreateRadioButton(m_moveBtn, path+"move.png", "Move selected object", index++, gcnew System::EventHandler(this, &GUIForm::MoveClick)); CreateRadioButton(m_rotateBtn, path+"rotate.png", "Rotate selected object", index++, gcnew System::EventHandler(this, &GUIForm::RotateClick)); CreateRadioButton(m_scaleBtn, path+"scale.png", "Scale selected object", index++, gcnew System::EventHandler(this, &GUIForm::ScaleClick)); CreateButton(m_removeBtn, path+"clear.png", "Clear all scene objects", index++, gcnew System::EventHandler(this, &GUIForm::RemoveClick)); m_disabledGravity = Image::FromFile(path+"dgravity.png"); m_enabledGravity = m_gravityBtn->BackgroundImage; index = 0; CreateSpinBox(m_vertRows, path+"vertrows.png", "Change the number of vertex rows", index++, 2.0, 4.0, 30.0, gcnew System::EventHandler(this, &GUIForm::VertexRowsChanged)); CreateSpinBox(m_iterations, path+"iterations.png", "Change the number of solve iterations", index++, 1.0, 1.0, 50.0, gcnew System::EventHandler(this, &GUIForm::IterationChanged)); CreateSpinBox(m_timestep, path+"timestep.png", "Change the cloth timestep", index++, 0.01, 0.1, 50.0, gcnew System::EventHandler(this, &GUIForm::TimestepChanged)); CreateSpinBox(m_spacing, path+"spacing.png", "Change the spacing between vertices", index++, 0.05, 0.1, 1.5, gcnew System::EventHandler(this, &GUIForm::SpacingChanged)); } /** * Creates a gui control * @param control The control to add to the gui * @param image The image filename for the control icon * @param tip A tooltip description for the control * @param index The index for the control, used to determine positioning */ void CreateControl(ButtonBase^ control, String^ image, String^ tip, int index) { const int buttonSize = 32; const int border = 2; m_guiPanel->Controls->Add(control); control->TabIndex = 0; control->TabStop = false; control->UseVisualStyleBackColor = false; control->BackgroundImage = Image::FromFile(image); control->Cursor = System::Windows::Forms::Cursors::Hand; control->Location = System::Drawing::Point(border, (index*buttonSize)+border); control->Margin = System::Windows::Forms::Padding(0); control->MaximumSize = System::Drawing::Size(buttonSize, buttonSize); control->MinimumSize = System::Drawing::Size(buttonSize, buttonSize); control->Size = System::Drawing::Size(buttonSize, buttonSize); control->FlatAppearance->BorderSize = 0; control->FlatStyle = System::Windows::Forms::FlatStyle::Flat; control->FlatAppearance->CheckedBackColor = m_pressedColor; control->FlatAppearance->MouseDownBackColor = m_pressedColor; control->FlatAppearance->MouseOverBackColor = Color::FromArgb(150, 150, 240); control->BackColor = m_unpressedColor; ToolTip^ tooltip = gcnew ToolTip(); tooltip->SetToolTip(control, tip); } /** * Creates a spinbox control * @param spinbox A tracker reference to the spinbox control * @param image The image filename for the spinbox icon * @param tip A tooltip description for the spinbox * @param index The index for the spinbox, used to determine positioning * @param increment The amount to increment the value when arrows are pressed * @param minimum The minimum value the spinbox can hold * @param maximum The maximum value the spinbox can hold * @param callback The method to call when the spinbox value changes */ void CreateSpinBox(SpinBox^% spinbox, String^ image, String^ tip, int index, double increment, double minimum, double maximum, EventHandler^ callback) { const int controlSize = 32; const int panelX = 680; const int panelY = 456 + (index*controlSize); const int panelHeight = controlSize-4; const int numericX = panelX + controlSize + 3; const int numericY = panelY + 5; spinbox = gcnew SpinBox(); spinbox->panel = gcnew Panel(); spinbox->numeric = gcnew NumericUpDown(); Form^ simForm = (Form^)*m_pinnedSimForm; simForm->Controls->Add(spinbox->panel); simForm->Controls->Add(spinbox->numeric); spinbox->panel->BackgroundImage = Image::FromFile(image); spinbox->panel->Location = System::Drawing::Point(panelX, panelY+2); spinbox->panel->Margin = System::Windows::Forms::Padding(0); spinbox->panel->MaximumSize = System::Drawing::Size(controlSize, panelHeight); spinbox->panel->MinimumSize = System::Drawing::Size(controlSize, panelHeight); spinbox->panel->Size = System::Drawing::Size(controlSize, panelHeight); spinbox->panel->TabIndex = 0; spinbox->panel->TabStop = false; spinbox->numeric->TabIndex = 0; spinbox->numeric->TabStop = false; spinbox->numeric->DecimalPlaces = 3; spinbox->numeric->Location = System::Drawing::Point(numericX, numericY); spinbox->numeric->Size = System::Drawing::Size(65, 22); spinbox->numeric->ReadOnly = true; spinbox->numeric->InterceptArrowKeys = false; spinbox->numeric->BackColor = System::Drawing::Color::Silver; spinbox->numeric->Increment = Decimal(increment); spinbox->numeric->Minimum = Decimal(minimum); spinbox->numeric->Maximum = Decimal(maximum); spinbox->numeric->ValueChanged += callback; ToolTip^ tooltip = gcnew ToolTip(); tooltip->SetToolTip(spinbox->panel, tip); } /** * Required method for Designer support */ #pragma region Windows Form Designer generated code void InitializeComponent(void) { this->m_mainPanel = (gcnew System::Windows::Forms::Panel()); this->m_guiPanel = (gcnew System::Windows::Forms::Panel()); this->m_mainPanel->SuspendLayout(); this->SuspendLayout(); // // m_mainPanel // this->m_mainPanel->Controls->Add(this->m_guiPanel); this->m_mainPanel->Location = System::Drawing::Point(0, 0); this->m_mainPanel->Name = L"m_mainPanel"; this->m_mainPanel->Size = System::Drawing::Size(800, 600); this->m_mainPanel->TabIndex = 0; // // m_guiPanel // this->m_guiPanel->BackColor = System::Drawing::Color::Gray; this->m_guiPanel->Location = System::Drawing::Point(12, 12); this->m_guiPanel->Name = L"m_guiPanel"; this->m_guiPanel->Size = System::Drawing::Size(36, 484); this->m_guiPanel->TabIndex = 14; // // GUIForm // this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::None; this->BackColor = System::Drawing::Color::DimGray; this->ClientSize = System::Drawing::Size(788, 588); this->Controls->Add(this->m_mainPanel); this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog; this->MaximumSize = System::Drawing::Size(806, 633); this->MinimumSize = System::Drawing::Size(806, 633); this->Name = L"GUIForm"; this->Text = L"Cloth Simulator"; this->FormClosed += gcnew FormClosedEventHandler(this, &GUIForm::GUIFormClosed); this->m_mainPanel->ResumeLayout(false); this->ResumeLayout(false); } #pragma endregion GuiCallbacks* m_callbacks; ///< Callbacks for the gui gcroot<Form^>* m_pinnedSimForm; ///< pinned as native needs window handle Image^ m_disabledGravity; ///< Image for disabled gravity Image^ m_enabledGravity; ///< Image for enabled gravity RadioButton^ m_toolSelected; ///< Which object manipulation tool selected Color m_unpressedColor; ///< Color of buttons when unpressed Color m_pressedColor; ///< Color of buttons when pressed CheckBox^ m_gravityBtn; ///< Checkbox for toggling on gravity CheckBox^ m_vertsBtn; ///< Checkbox for toggling on vertices CheckBox^ m_handleBtn; ///< Checkbox for toggling on handle mode CheckBox^ m_wireBtn; ///< Checkbox for toggling on wireframe mode RadioButton^ m_scaleBtn; ///< Checkbox for scaling the selected scene object RadioButton^ m_rotateBtn; ///< Checkbox for rotating the selected scene object RadioButton^ m_moveBtn; ///< Checkbox for moving the selected scene object RadioButton^ m_animateBtn; ///< Checkbox for animating an object Button^ m_resetCamBtn; ///< Button for resetting the camera Button^ m_resetClothBtn; ///< Button for resetting the cloth Button^ m_unpinBtn; ///< Button for upinning the cloth Button^ m_cynlinderBtn; ///< Button for creating a cynlinder Button^ m_sphereBtn; ///< Button for creating a sphere Button^ m_boxBtn; ///< Button for creating a box Button^ m_removeBtn; ///< Button for clearing all scene objects SpinBox^ m_vertRows; ///< Spinbox for changing vertex row number SpinBox^ m_iterations; ///< Spinbox for changing iterations SpinBox^ m_timestep; ///< Spinbox for changing the timestep SpinBox^ m_spacing; ///< Spinbox for changing the spacing between vertices /** * Designer specific components */ System::ComponentModel::Container^ components; ///< Auto-Required designer variable. System::Windows::Forms::Panel^ m_mainPanel; ///< Panel for holding main simulation form System::Windows::Forms::Panel^ m_guiPanel; ///< Panel for gui buttons }; }