Make PROP tool less annoying to use
By refusing to exit unless the property being set is correct, rather than throwing an error when it's not and exiting anyway. It's still possible to cancel changing the current setting by exiting with Esc.
This commit is contained in:
parent
6a64de8297
commit
3e4fed02d4
src/gui/game
@ -33,8 +33,11 @@ public:
|
|||||||
PropertyTool * tool;
|
PropertyTool * tool;
|
||||||
Simulation *sim;
|
Simulation *sim;
|
||||||
std::vector<StructProperty> properties;
|
std::vector<StructProperty> properties;
|
||||||
|
std::optional<PropertyTool::Configuration> configuration;
|
||||||
PropertyWindow(PropertyTool *tool_, Simulation *sim);
|
PropertyWindow(PropertyTool *tool_, Simulation *sim);
|
||||||
void SetProperty(bool warn);
|
void SetProperty();
|
||||||
|
void CheckProperty();
|
||||||
|
void Update();
|
||||||
void OnDraw() override;
|
void OnDraw() override;
|
||||||
void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override;
|
void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override;
|
||||||
void OnTryExit(ExitMethod method) override;
|
void OnTryExit(ExitMethod method) override;
|
||||||
@ -59,18 +62,18 @@ sim(sim_)
|
|||||||
okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
okayButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
||||||
okayButton->Appearance.BorderInactive = ui::Colour(200, 200, 200);
|
okayButton->Appearance.BorderInactive = ui::Colour(200, 200, 200);
|
||||||
okayButton->SetActionCallback({ [this] {
|
okayButton->SetActionCallback({ [this] {
|
||||||
if (textField->GetText().length())
|
CloseActiveWindow();
|
||||||
{
|
SetProperty();
|
||||||
CloseActiveWindow();
|
SelfDestruct();
|
||||||
SetProperty(true);
|
|
||||||
SelfDestruct();
|
|
||||||
}
|
|
||||||
} });
|
} });
|
||||||
AddComponent(okayButton);
|
AddComponent(okayButton);
|
||||||
SetOkayButton(okayButton);
|
SetOkayButton(okayButton);
|
||||||
|
|
||||||
property = new ui::DropDown(ui::Point(8, 25), ui::Point(Size.X-16, 16));
|
property = new ui::DropDown(ui::Point(8, 25), ui::Point(Size.X-16, 16));
|
||||||
property->SetActionCallback({ [this] { FocusComponent(textField); } });
|
property->SetActionCallback({ [this] {
|
||||||
|
FocusComponent(textField);
|
||||||
|
Update();
|
||||||
|
} });
|
||||||
AddComponent(property);
|
AddComponent(property);
|
||||||
for (int i = 0; i < int(properties.size()); i++)
|
for (int i = 0; i < int(properties.size()); i++)
|
||||||
{
|
{
|
||||||
@ -84,147 +87,159 @@ sim(sim_)
|
|||||||
textField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
textField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
||||||
textField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
textField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
||||||
textField->SetText(prefs.Get("Prop.Value", String("")));
|
textField->SetText(prefs.Get("Prop.Value", String("")));
|
||||||
|
textField->SetActionCallback({ [this]() {
|
||||||
|
Update();
|
||||||
|
} });
|
||||||
AddComponent(textField);
|
AddComponent(textField);
|
||||||
FocusComponent(textField);
|
FocusComponent(textField);
|
||||||
SetProperty(false);
|
Update();
|
||||||
|
|
||||||
MakeActiveWindow();
|
MakeActiveWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropertyWindow::SetProperty(bool warn)
|
void PropertyWindow::Update()
|
||||||
{
|
{
|
||||||
tool->validProperty = false;
|
CheckProperty();
|
||||||
if(property->GetOption().second!=-1 && textField->GetText().length() > 0)
|
auto haveConfiguration = bool(configuration);
|
||||||
{
|
okayButton->Enabled = haveConfiguration;
|
||||||
tool->validProperty = true;
|
textField->SetTextColour(haveConfiguration ? ui::Colour(255, 255, 255) : style::Colour::ErrorTitle);
|
||||||
String value = textField->GetText().ToUpper();
|
}
|
||||||
try {
|
|
||||||
switch(properties[property->GetOption().second].Type)
|
|
||||||
{
|
|
||||||
case StructProperty::Integer:
|
|
||||||
case StructProperty::ParticleType:
|
|
||||||
{
|
|
||||||
int v;
|
|
||||||
if(value.length() > 2 && value.BeginsWith("0X"))
|
|
||||||
{
|
|
||||||
//0xC0FFEE
|
|
||||||
v = value.Substr(2).ToNumber<unsigned int>(Format::Hex());
|
|
||||||
}
|
|
||||||
else if(value.length() > 1 && value.BeginsWith("#"))
|
|
||||||
{
|
|
||||||
//#C0FFEE
|
|
||||||
v = value.Substr(1).ToNumber<unsigned int>(Format::Hex());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Try to parse as particle name
|
|
||||||
v = sim->GetParticleType(value.ToUtf8());
|
|
||||||
|
|
||||||
// Try to parse special GoL rules
|
void PropertyWindow::CheckProperty()
|
||||||
if (v == -1 && properties[property->GetOption().second].Name == "ctype")
|
{
|
||||||
|
configuration.reset();
|
||||||
|
PropertyTool::Configuration newConfiguration;
|
||||||
|
if (!(property->GetOption().second!=-1 && textField->GetText().length() > 0))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String value = textField->GetText().ToUpper();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch(properties[property->GetOption().second].Type)
|
||||||
|
{
|
||||||
|
case StructProperty::Integer:
|
||||||
|
case StructProperty::ParticleType:
|
||||||
|
{
|
||||||
|
int v;
|
||||||
|
if(value.length() > 2 && value.BeginsWith("0X"))
|
||||||
|
{
|
||||||
|
//0xC0FFEE
|
||||||
|
v = value.Substr(2).ToNumber<unsigned int>(Format::Hex());
|
||||||
|
}
|
||||||
|
else if(value.length() > 1 && value.BeginsWith("#"))
|
||||||
|
{
|
||||||
|
//#C0FFEE
|
||||||
|
v = value.Substr(1).ToNumber<unsigned int>(Format::Hex());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Try to parse as particle name
|
||||||
|
v = sim->GetParticleType(value.ToUtf8());
|
||||||
|
|
||||||
|
// Try to parse special GoL rules
|
||||||
|
if (v == -1 && properties[property->GetOption().second].Name == "ctype")
|
||||||
|
{
|
||||||
|
if (value.length() > 1 && value.BeginsWith("B") && value.Contains("/"))
|
||||||
{
|
{
|
||||||
if (value.length() > 1 && value.BeginsWith("B") && value.Contains("/"))
|
v = ParseGOLString(value);
|
||||||
|
if (v == -1)
|
||||||
{
|
{
|
||||||
v = ParseGOLString(value);
|
class InvalidGOLString : public std::exception
|
||||||
if (v == -1)
|
|
||||||
{
|
{
|
||||||
class InvalidGOLString : public std::exception
|
};
|
||||||
{
|
throw InvalidGOLString();
|
||||||
};
|
|
||||||
throw InvalidGOLString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v = sim->GetParticleType(value.ToUtf8());
|
||||||
|
if (v == -1)
|
||||||
{
|
{
|
||||||
v = sim->GetParticleType(value.ToUtf8());
|
for (auto *elementTool : tool->gameModel.GetMenuList()[SC_LIFE]->GetToolList())
|
||||||
if (v == -1)
|
|
||||||
{
|
{
|
||||||
for (auto *elementTool : tool->gameModel.GetMenuList()[SC_LIFE]->GetToolList())
|
if (elementTool && elementTool->Name == value)
|
||||||
{
|
{
|
||||||
if (elementTool && elementTool->Name == value)
|
v = ID(elementTool->ToolID);
|
||||||
{
|
break;
|
||||||
v = ID(elementTool->ToolID);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse as plain number
|
|
||||||
if (v == -1)
|
|
||||||
{
|
|
||||||
v = value.ToNumber<int>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties[property->GetOption().second].Name == "type" && (v < 0 || v >= PT_NUM || !sim->elements[v].Enabled))
|
// Parse as plain number
|
||||||
|
if (v == -1)
|
||||||
{
|
{
|
||||||
tool->validProperty = false;
|
v = value.ToNumber<int>();
|
||||||
if (warn)
|
|
||||||
new ErrorMessage("Could not set property", "Invalid particle type");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if constexpr (DEBUG)
|
|
||||||
{
|
|
||||||
std::cout << "Got int value " << v << std::endl;
|
|
||||||
}
|
|
||||||
tool->propValue.Integer = v;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case StructProperty::UInteger:
|
|
||||||
|
if (properties[property->GetOption().second].Name == "type" && (v < 0 || v >= PT_NUM || !sim->elements[v].Enabled))
|
||||||
{
|
{
|
||||||
unsigned int v;
|
|
||||||
if(value.length() > 2 && value.BeginsWith("0X"))
|
|
||||||
{
|
|
||||||
//0xC0FFEE
|
|
||||||
v = value.Substr(2).ToNumber<unsigned int>(Format::Hex());
|
|
||||||
}
|
|
||||||
else if(value.length() > 1 && value.BeginsWith("#"))
|
|
||||||
{
|
|
||||||
//#C0FFEE
|
|
||||||
v = value.Substr(1).ToNumber<unsigned int>(Format::Hex());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
v = value.ToNumber<unsigned int>();
|
|
||||||
}
|
|
||||||
if constexpr (DEBUG)
|
|
||||||
{
|
|
||||||
std::cout << "Got uint value " << v << std::endl;
|
|
||||||
}
|
|
||||||
tool->propValue.UInteger = v;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case StructProperty::Float:
|
|
||||||
{
|
|
||||||
if (properties[property->GetOption().second].Name == "temp")
|
|
||||||
tool->propValue.Float = format::StringToTemperature(value, tool->gameModel.GetTemperatureScale());
|
|
||||||
else
|
|
||||||
tool->propValue.Float = value.ToNumber<float>();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
tool->validProperty = false;
|
|
||||||
if (warn)
|
|
||||||
new ErrorMessage("Could not set property", "Invalid property");
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
if constexpr (DEBUG)
|
||||||
|
{
|
||||||
|
std::cout << "Got int value " << v << std::endl;
|
||||||
|
}
|
||||||
|
newConfiguration.propValue.Integer = v;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
tool->propOffset = properties[property->GetOption().second].Offset;
|
case StructProperty::UInteger:
|
||||||
tool->propType = properties[property->GetOption().second].Type;
|
{
|
||||||
tool->changeType = properties[property->GetOption().second].Name == "type";
|
unsigned int v;
|
||||||
} catch (const std::exception& ex) {
|
if(value.length() > 2 && value.BeginsWith("0X"))
|
||||||
tool->validProperty = false;
|
{
|
||||||
if (warn)
|
//0xC0FFEE
|
||||||
new ErrorMessage("Could not set property", "Invalid value provided");
|
v = value.Substr(2).ToNumber<unsigned int>(Format::Hex());
|
||||||
return;
|
}
|
||||||
}
|
else if(value.length() > 1 && value.BeginsWith("#"))
|
||||||
{
|
{
|
||||||
auto &prefs = GlobalPrefs::Ref();
|
//#C0FFEE
|
||||||
Prefs::DeferWrite dw(prefs);
|
v = value.Substr(1).ToNumber<unsigned int>(Format::Hex());
|
||||||
prefs.Set("Prop.Type", property->GetOption().second);
|
}
|
||||||
prefs.Set("Prop.Value", textField->GetText());
|
else
|
||||||
|
{
|
||||||
|
v = value.ToNumber<unsigned int>();
|
||||||
|
}
|
||||||
|
if constexpr (DEBUG)
|
||||||
|
{
|
||||||
|
std::cout << "Got uint value " << v << std::endl;
|
||||||
|
}
|
||||||
|
newConfiguration.propValue.UInteger = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case StructProperty::Float:
|
||||||
|
{
|
||||||
|
if (properties[property->GetOption().second].Name == "temp")
|
||||||
|
newConfiguration.propValue.Float = format::StringToTemperature(value, tool->gameModel.GetTemperatureScale());
|
||||||
|
else
|
||||||
|
newConfiguration.propValue.Float = value.ToNumber<float>();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
newConfiguration.propOffset = properties[property->GetOption().second].Offset;
|
||||||
|
newConfiguration.propType = properties[property->GetOption().second].Type;
|
||||||
|
newConfiguration.changeType = properties[property->GetOption().second].Name == "type";
|
||||||
|
}
|
||||||
|
catch (const std::exception& ex)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
configuration = newConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyWindow::SetProperty()
|
||||||
|
{
|
||||||
|
tool->configuration = configuration;
|
||||||
|
{
|
||||||
|
auto &prefs = GlobalPrefs::Ref();
|
||||||
|
Prefs::DeferWrite dw(prefs);
|
||||||
|
prefs.Set("Prop.Type", property->GetOption().second);
|
||||||
|
prefs.Set("Prop.Value", textField->GetText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +272,7 @@ void PropertyTool::OpenWindow(Simulation *sim)
|
|||||||
|
|
||||||
void PropertyTool::SetProperty(Simulation *sim, ui::Point position)
|
void PropertyTool::SetProperty(Simulation *sim, ui::Point position)
|
||||||
{
|
{
|
||||||
if(position.X<0 || position.X>XRES || position.Y<0 || position.Y>YRES || !validProperty)
|
if(position.X<0 || position.X>XRES || position.Y<0 || position.Y>YRES || !configuration)
|
||||||
return;
|
return;
|
||||||
int i = sim->pmap[position.Y][position.X];
|
int i = sim->pmap[position.Y][position.X];
|
||||||
if(!i)
|
if(!i)
|
||||||
@ -265,23 +280,23 @@ void PropertyTool::SetProperty(Simulation *sim, ui::Point position)
|
|||||||
if(!i)
|
if(!i)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (changeType)
|
if (configuration->changeType)
|
||||||
{
|
{
|
||||||
sim->part_change_type(ID(i), int(sim->parts[ID(i)].x+0.5f), int(sim->parts[ID(i)].y+0.5f), propValue.Integer);
|
sim->part_change_type(ID(i), int(sim->parts[ID(i)].x+0.5f), int(sim->parts[ID(i)].y+0.5f), configuration->propValue.Integer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (propType)
|
switch (configuration->propType)
|
||||||
{
|
{
|
||||||
case StructProperty::Float:
|
case StructProperty::Float:
|
||||||
*((float*)(((char*)&sim->parts[ID(i)])+propOffset)) = propValue.Float;
|
*((float*)(((char*)&sim->parts[ID(i)])+configuration->propOffset)) = configuration->propValue.Float;
|
||||||
break;
|
break;
|
||||||
case StructProperty::ParticleType:
|
case StructProperty::ParticleType:
|
||||||
case StructProperty::Integer:
|
case StructProperty::Integer:
|
||||||
*((int*)(((char*)&sim->parts[ID(i)])+propOffset)) = propValue.Integer;
|
*((int*)(((char*)&sim->parts[ID(i)])+configuration->propOffset)) = configuration->propValue.Integer;
|
||||||
break;
|
break;
|
||||||
case StructProperty::UInteger:
|
case StructProperty::UInteger:
|
||||||
*((unsigned int*)(((char*)&sim->parts[ID(i)])+propOffset)) = propValue.UInteger;
|
*((unsigned int*)(((char*)&sim->parts[ID(i)])+configuration->propOffset)) = configuration->propValue.UInteger;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -375,6 +390,6 @@ void PropertyTool::DrawRect(Simulation *sim, Brush const &cBrush, ui::Point posi
|
|||||||
|
|
||||||
void PropertyTool::DrawFill(Simulation *sim, Brush const &cBrush, ui::Point position)
|
void PropertyTool::DrawFill(Simulation *sim, Brush const &cBrush, ui::Point position)
|
||||||
{
|
{
|
||||||
if (validProperty)
|
if (configuration)
|
||||||
sim->flood_prop(position.X, position.Y, propOffset, propValue, propType);
|
sim->flood_prop(position.X, position.Y, configuration->propOffset, configuration->propValue, configuration->propType);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <memory>
|
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include "common/Vec2.h"
|
#include "common/Vec2.h"
|
||||||
#include "graphics/Pixel.h"
|
#include "graphics/Pixel.h"
|
||||||
#include "gui/interface/Point.h"
|
#include "gui/interface/Point.h"
|
||||||
#include "simulation/StructProperty.h"
|
#include "simulation/StructProperty.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
class Simulation;
|
class Simulation;
|
||||||
class Brush;
|
class Brush;
|
||||||
@ -97,12 +98,18 @@ public:
|
|||||||
|
|
||||||
class PropertyTool: public Tool
|
class PropertyTool: public Tool
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
struct Configuration
|
||||||
|
{
|
||||||
|
StructProperty::PropertyType propType;
|
||||||
|
PropertyValue propValue;
|
||||||
|
bool changeType;
|
||||||
|
size_t propOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
GameModel &gameModel;
|
GameModel &gameModel;
|
||||||
StructProperty::PropertyType propType;
|
std::optional<Configuration> configuration;
|
||||||
PropertyValue propValue;
|
|
||||||
bool changeType;
|
|
||||||
size_t propOffset;
|
|
||||||
bool validProperty;
|
|
||||||
|
|
||||||
friend class PropertyWindow;
|
friend class PropertyWindow;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user