Răsfoiți Sursa

Added non-linear slider for better simturn duration selection

Ivan Savenko 1 an în urmă
părinte
comite
c5eeaa6526

+ 2 - 1
Mods/vcmi/config/vcmi/english.json

@@ -236,7 +236,8 @@
 	"vcmi.optionsTab.simturnsMin.hover" : "At least for",
 	"vcmi.optionsTab.simturnsMax.hover" : "At most for",
 	"vcmi.optionsTab.simturnsAI.hover" : "(Experimental) Simultaneous AI Turns",
-	"vcmi.optionsTab.simturns.help" : "Play simultaneously at least for specified number of days, after which simultaneous turns will stay active until specified day or until players make contact. Contacts between players during this period are blocked",
+	"vcmi.optionsTab.simturnsMin.help" : "Play simultaneously for specified number of days. Contacts between players during this period are blocked",
+	"vcmi.optionsTab.simturnsMax.help" : "Play simultaneously for specified number of days or until contact with another player",
 	"vcmi.optionsTab.simturnsAI.help" : "",
 	
 	// Translation note: translate strings below using form that is correct for "0 days", "1 day" and "2 days" in your language

+ 17 - 4
client/gui/InterfaceObjectConfigurable.cpp

@@ -501,12 +501,25 @@ std::shared_ptr<CSlider> InterfaceObjectConfigurable::buildSlider(const JsonNode
 	auto position = readPosition(config["position"]);
 	int length = config["size"].Integer();
 	auto style = config["style"].String() == "brown" ? CSlider::BROWN : CSlider::BLUE;
-	auto itemsVisible = config["itemsVisible"].Integer();
-	auto itemsTotal = config["itemsTotal"].Integer();
 	auto value = config["selected"].Integer();
 	bool horizontal = config["orientation"].String() == "horizontal";
-	const auto & result =
-		std::make_shared<CSlider>(position, length, callbacks_int.at(config["callback"].String()), itemsVisible, itemsTotal, value, horizontal ? Orientation::HORIZONTAL : Orientation::VERTICAL, style);
+	auto orientation = horizontal ? Orientation::HORIZONTAL : Orientation::VERTICAL;
+
+	std::shared_ptr<CSlider> result;
+
+	if (config["items"].isNull())
+	{
+		auto itemsVisible = config["itemsVisible"].Integer();
+		auto itemsTotal = config["itemsTotal"].Integer();
+
+		result = std::make_shared<CSlider>(position, length, callbacks_int.at(config["callback"].String()), itemsVisible, itemsTotal, value, orientation, style);
+	}
+	else
+	{
+		auto items = config["items"].convertTo<std::vector<int>>();
+		result = std::make_shared<SliderNonlinear>(position, length, callbacks_int.at(config["callback"].String()), items, value, orientation, style);
+	}
+
 
 	if(!config["scrollBounds"].isNull())
 	{

+ 12 - 4
client/lobby/OptionsTabBase.cpp

@@ -42,12 +42,14 @@ OptionsTabBase::OptionsTabBase(const JsonPath & configPath)
 	addCallback("setSimturnDurationMin", [&](int index){
 		SimturnsInfo info = SEL->getStartInfo()->simturnsInfo;
 		info.requiredTurns = index;
+		info.optionalTurns = std::max(info.optionalTurns, index);
 		CSH->setSimturnsInfo(info);
 	});
 
 	addCallback("setSimturnDurationMax", [&](int index){
 		SimturnsInfo info = SEL->getStartInfo()->simturnsInfo;
 		info.optionalTurns = index;
+		info.requiredTurns = std::min(info.requiredTurns, index);
 		CSH->setSimturnsInfo(info);
 	});
 
@@ -192,14 +194,19 @@ void OptionsTabBase::recreate()
 {
 	auto const & generateSimturnsDurationText = [](int days) -> std::string
 	{
+		if (days == 0)
+			return CGI->generaltexth->translate("core.genrltxt.523");
+
+		if (days >= 1000000) // Not "unlimited" but close enough
+			return CGI->generaltexth->translate("core.turndur.10");
+
 		bool canUseMonth = days % 28 == 0 && days >= 28*2;
 		bool canUseWeek = days % 7 == 0 && days >= 7*2;
 
-		MetaString message;
 		int value = days;
 		std::string text = "vcmi.optionsTab.simturns.days";
 
-		if (canUseWeek)
+		if (canUseWeek && !canUseMonth)
 		{
 			value = days / 7;
 			text = "vcmi.optionsTab.simturns.weeks";
@@ -211,6 +218,7 @@ void OptionsTabBase::recreate()
 			text = "vcmi.optionsTab.simturns.months";
 		}
 
+		MetaString message;
 		message.appendTextID(Languages::getPluralFormTextID( CGI->generaltexth->getPreferredLanguage(), value, text));
 		message.replaceNumber(value);
 		return message.toString();
@@ -218,10 +226,10 @@ void OptionsTabBase::recreate()
 
 	//Simultaneous turns
 	if(auto turnSlider = widget<CSlider>("simturnsDurationMin"))
-		turnSlider->scrollTo(SEL->getStartInfo()->simturnsInfo.requiredTurns);
+		turnSlider->setValue(SEL->getStartInfo()->simturnsInfo.requiredTurns);
 
 	if(auto turnSlider = widget<CSlider>("simturnsDurationMax"))
-		turnSlider->scrollTo(SEL->getStartInfo()->simturnsInfo.optionalTurns);
+		turnSlider->setValue(SEL->getStartInfo()->simturnsInfo.optionalTurns);
 
 	if(auto w = widget<CLabel>("labelSimturnsDurationValueMin"))
 		w->setText(generateSimturnsDurationText(SEL->getStartInfo()->simturnsInfo.requiredTurns));

+ 35 - 2
client/widgets/Slider.cpp

@@ -69,6 +69,11 @@ int CSlider::getValue() const
 	return value;
 }
 
+void CSlider::setValue(int to)
+{
+	scrollTo(value);
+}
+
 int CSlider::getCapacity() const
 {
 	return capacity;
@@ -119,7 +124,7 @@ void CSlider::scrollTo(int to)
 
 	updateSliderPos();
 
-	moved(to);
+	moved(getValue());
 }
 
 void CSlider::clickPressed(const Point & cursorPosition)
@@ -164,7 +169,7 @@ bool CSlider::receiveEvent(const Point &position, int eventType) const
 	return testTarget.isInside(position);
 }
 
-CSlider::CSlider(Point position, int totalw, std::function<void(int)> Moved, int Capacity, int Amount, int Value, Orientation orientation, CSlider::EStyle style)
+CSlider::CSlider(Point position, int totalw, const std::function<void(int)> & Moved, int Capacity, int Amount, int Value, Orientation orientation, CSlider::EStyle style)
 	: Scrollable(LCLICK | DRAG, position, orientation ),
 	capacity(Capacity),
 	amount(Amount),
@@ -297,3 +302,31 @@ void CSlider::scrollToMax()
 {
 	scrollTo(amount);
 }
+
+SliderNonlinear::SliderNonlinear(Point position, int length, const std::function<void(int)> & Moved, const std::vector<int> & values, int Value, Orientation orientation, EStyle style)
+	: CSlider(position, length, Moved, 1, values.size(), Value, orientation, style)
+	, scaledValues(values)
+{
+
+}
+
+int SliderNonlinear::getValue() const
+{
+	return scaledValues.at(CSlider::getValue());
+}
+
+void SliderNonlinear::setValue(int to)
+{
+	size_t nearest = 0;
+
+	for(size_t i = 0; i < scaledValues.size(); ++i)
+	{
+		int nearestDistance = std::abs(to - scaledValues[nearest]);
+		int currentDistance = std::abs(to - scaledValues[i]);
+
+		if(currentDistance < nearestDistance)
+			nearest = i;
+	}
+
+	scrollTo(nearest);
+}

+ 16 - 2
client/widgets/Slider.h

@@ -59,10 +59,11 @@ public:
 
 	/// Amount modifier
 	void setAmount(int to);
+	virtual void setValue(int to);
 
 	/// Accessors
 	int getAmount() const;
-	int getValue() const;
+	virtual int getValue() const;
 	int getCapacity() const;
 
 	void addCallback(std::function<void(int)> callback);
@@ -80,7 +81,20 @@ public:
 	 /// @param Capacity maximal number of visible at once elements
 	 /// @param Amount total amount of elements, including not visible
 	 /// @param Value starting position
-	CSlider(Point position, int length, std::function<void(int)> Moved, int Capacity, int Amount,
+	CSlider(Point position, int length, const std::function<void(int)> & Moved, int Capacity, int Amount,
 		int Value, Orientation orientation, EStyle style = BROWN);
 	~CSlider();
 };
+
+class SliderNonlinear : public CSlider
+{
+	/// If non-empty then slider has non-linear values, e.g. if slider is at position 5 out of 10 then actual "value" is not 5, but 5th value in this vector
+	std::vector<int> scaledValues;
+
+	using CSlider::setAmount; // make private
+public:
+	void setValue(int to) override;
+	int getValue() const override;
+
+	SliderNonlinear(Point position, int length, const std::function<void(int)> & Moved, const std::vector<int> & values, int Value, Orientation orientation, EStyle style);
+};

+ 32 - 13
config/widgets/turnOptionsTab.json

@@ -68,23 +68,35 @@
 			"type": "texture",
 			"image": "DiBoxBck",
 			"color" : "blue", 
-			"rect": {"x" : 64, "y" : 416, "w": 316, "h": 102}
+			"rect": {"x" : 64, "y" : 394, "w": 316, "h": 124}
 		},
 		{
 			"type": "transparentFilledRectangle",
-			"rect": {"x" : 64, "y" : 416, "w": 316, "h": 102},
+			"rect": {"x" : 64, "y" : 394, "w": 316, "h": 124},
 			"color": [0, 0, 0, 0],
 			"colorLine": [64, 80, 128, 128]
 		},
 		{
 			"type": "transparentFilledRectangle",
-			"rect": {"x" : 64, "y" : 465, "w": 316, "h": 1},
+			"rect": {"x" : 65, "y" : 416, "w": 314, "h": 1},
 			"color": [0, 0, 0, 0],
 			"colorLine": [80, 96, 160, 128]
 		},
 		{
 			"type": "transparentFilledRectangle",
-			"rect": {"x" : 64, "y" : 466, "w": 316, "h": 1},
+			"rect": {"x" : 65, "y" : 417, "w": 314, "h": 1},
+			"color": [0, 0, 0, 0],
+			"colorLine": [32, 40, 128, 128]
+		},
+		{
+			"type": "transparentFilledRectangle",
+			"rect": {"x" : 65, "y" : 466, "w": 314, "h": 1},
+			"color": [0, 0, 0, 0],
+			"colorLine": [80, 96, 160, 128]
+		},
+		{
+			"type": "transparentFilledRectangle",
+			"rect": {"x" : 65, "y" : 467, "w": 314, "h": 1},
 			"color": [0, 0, 0, 0],
 			"colorLine": [32, 40, 128, 128]
 		},
@@ -189,7 +201,7 @@
 			"alignment": "left",
 			"color": "white",
 			"text": "vcmi.optionsTab.simturnsMax.hover",
-			"position": {"x": 70, "y": 445}
+			"position": {"x": 70, "y": 470}
 		},
 
 		{
@@ -199,8 +211,7 @@
 			"position": {"x": 178, "y": 420},
 			"size": 200,
 			"callback": "setSimturnDurationMin",
-			"itemsVisible": 1,
-			"itemsTotal": 28,
+			"items": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 21, 28, 35, 42, 49, 56, 84, 112, 140, 168 ],
 			"selected": 0,
 			"style": "blue",
 			"scrollBounds": {"x": 0, "y": 0, "w": 194, "h": 32},
@@ -210,11 +221,10 @@
 			"name": "simturnsDurationMax",
 			"type": "slider",
 			"orientation": "horizontal",
-			"position": {"x": 178, "y": 445},
+			"position": {"x": 178, "y": 470},
 			"size": 200,
 			"callback": "setSimturnDurationMax",
-			"itemsVisible": 1,
-			"itemsTotal": 28,
+			"items": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 21, 28, 35, 42, 49, 56, 84, 112, 140, 168, 1000000 ],
 			"selected": 0,
 			"style": "blue",
 			"scrollBounds": {"x": 0, "y": 0, "w": 194, "h": 32},
@@ -236,16 +246,25 @@
 			"alignment": "center",
 			"color": "white",
 			"text": "",
-			"position": {"x": 320, "y": 453}
+			"position": {"x": 320, "y": 478}
+		},
+		{
+			"type" : "label",
+			"text": "vcmi.optionsTab.simturnsMin.help",
+			"type": "multiLineLabel",
+			"font": "tiny",
+			"alignment": "center",
+			"color": "white",
+			"rect": {"x": 70, "y": 430, "w": 300, "h": 40}
 		},
 		{
 			"type" : "label",
-			"text": "vcmi.optionsTab.simturns.help",
+			"text": "vcmi.optionsTab.simturnsMax.help",
 			"type": "multiLineLabel",
 			"font": "tiny",
 			"alignment": "center",
 			"color": "white",
-			"rect": {"x": 70, "y": 470, "w": 300, "h": 40}
+			"rect": {"x": 70, "y": 480, "w": 300, "h": 40}
 		},
 		{
 			"position": {"x": 70, "y": 535},