Bläddra i källkod

Merge pull request #6329 from Laserlicht/graphical_fixes

Content aware sliders
Ivan Savenko 2 veckor sedan
förälder
incheckning
f3fd5c05af

+ 1 - 1
Mods/vcmi/Content/config/german.json

@@ -139,7 +139,7 @@
 	"vcmi.lobby.deleteFolder" : "Möchtet Ihr folgenden Ordner löschen?",
 	"vcmi.lobby.deleteMode" : "In den Löschmodus wechseln und zurück",
 	"vcmi.lobby.battleOnlyMode" : "Nur Kämpfen Modus",
-	"vcmi.lobby.battleOnlyModeSubTitle" : "Wähle Helden, Armeen, Skills, Artefakte und ein Schlachtfeld für einen einfachen Kampf ohne Abenteuerkarte",
+	"vcmi.lobby.battleOnlyModeSubTitle" : "Wähle Helden, Armeen, Skills, Artefakte und ein Schlachtfeld für einen Kampf ohne Abenteuerkarte",
 	"vcmi.lobby.battleOnlyModeBattlefield" : "Schlachtfeld",
 	"vcmi.lobby.battleOnlyModeBattlefieldSelect" : "Schlachtfeld auswählen",
 	"vcmi.lobby.battleOnlyModeHeroSelect" : "Helden auswählen",

+ 110 - 0
client/render/AssetGenerator.cpp

@@ -636,6 +636,116 @@ AssetGenerator::AnimationLayoutMap AssetGenerator::createAdventureMapButton(cons
 	return layout;
 }
 
+AssetGenerator::AnimationLayoutMap AssetGenerator::createSliderBar(bool brown, bool horizontal, int length)
+{
+	AnimationLayoutMap layout;
+
+	AnimationPath anim = brown ? AnimationPath::builtin("IGPCRDIV.DEF") : AnimationPath::builtin("SCNRBSL.DEF");
+
+	auto genSlider = [horizontal, length](std::shared_ptr<IImage> src, Canvas & canvas){
+		const int border = 6;
+		const int inner  = 4;
+		int pos = 0;
+
+		while (pos < length)
+		{
+			int remain = length - pos;
+			Rect c;
+
+			// FIRST segment → leading border + inner
+			if(pos == 0)
+			{
+				int w = std::min(remain, border + inner);
+				c = horizontal ? Rect(0, 0, w, 16)
+							: Rect(0, 0, 16, w);
+			}
+			// LAST segment → inner + trailing border
+			else if(remain <= border + inner)
+			{
+				int w = std::min(remain, border + inner);
+				c = horizontal ? Rect(16 - w, 0, w, 16)
+							: Rect(0, 16 - w, 16, w);
+			}
+			// MIDDLE → pure inner (no borders)
+			else
+			{
+				c = horizontal ? Rect(border, 0, inner, 16)
+							: Rect(0, border, 16, inner);
+			}
+
+			canvas.draw(src,
+						horizontal ? Point(pos,0) : Point(0,pos),
+						c);
+
+			// **Important**: advance by INNER for all but the last slice
+			if(remain > border + inner) pos += inner;
+			else break;
+		}
+	};
+
+	for(int i = 0; i < 4; i++)
+	{
+		std::string baseName = "Slider-" + std::string(brown ? "brown" : "blue") + "-" + std::string(horizontal ? "horizontal" : "vertical") + "-" + std::to_string(length) + "-" + std::to_string(i);
+		ImagePath spriteName = ImagePath::builtin(baseName + ".png");
+
+		imageFiles[spriteName] = [anim, horizontal, brown, length, i, genSlider](){
+			auto newImg = ENGINE->renderHandler().createImage(Point(horizontal ? length : 16, horizontal ? 16 : length), CanvasScalingPolicy::IGNORE);
+			auto canvas = newImg->getCanvas();
+			switch(i)
+			{
+			case 0:
+				{
+					auto src = ENGINE->renderHandler().loadAnimation(anim, EImageBlitMode::OPAQUE)->getImage(brown ? 4 : 0);
+					genSlider(src, canvas);
+					return newImg;
+				}
+			case 1:
+				{
+					auto tmpImg = ENGINE->renderHandler().createImage(Point(16, 16), CanvasScalingPolicy::IGNORE);
+					auto tmpCanvas = tmpImg->getCanvas();
+					auto srcImg = ENGINE->renderHandler().loadAnimation(anim, EImageBlitMode::OPAQUE)->getImage(brown ? 4 : 1);
+
+					tmpCanvas.drawColor(Rect(Point(0, 0), tmpImg->dimensions()), Colors::BLACK);
+					if(brown)
+					{
+						
+						tmpCanvas.draw(srcImg, Point(1, 1));
+						tmpCanvas.drawColorBlended(Rect(0, 0, tmpImg->width(), 3), ColorRGBA(0, 0, 0, 160));
+						tmpCanvas.drawColorBlended(Rect(0, 0, 3, tmpImg->height()), ColorRGBA(0, 0, 0, 160));
+					}
+					else
+						tmpCanvas.draw(srcImg, Point(0, 0));
+					genSlider(tmpImg, canvas);
+					return newImg;
+				}
+			case 2:
+				{
+					auto tmpImg = ENGINE->renderHandler().createImage(Point(16, 16), CanvasScalingPolicy::IGNORE);
+					auto tmpCanvas = tmpImg->getCanvas();
+					tmpCanvas.draw(ENGINE->renderHandler().loadAnimation(anim, EImageBlitMode::OPAQUE)->getImage(brown ? 4 : 2), Point(0, 0));
+					// TODO: generate disabled brown slider (not used yet, but filling with dummy avoids warning)
+					genSlider(tmpImg, canvas);
+					return newImg;
+				}
+			default:
+				{
+					auto tmpImg = ENGINE->renderHandler().createImage(Point(16, 16), CanvasScalingPolicy::IGNORE);
+					auto tmpCanvas = tmpImg->getCanvas();
+					tmpCanvas.draw(ENGINE->renderHandler().loadAnimation(anim, EImageBlitMode::OPAQUE)->getImage(brown ? 4 : 3), Point(0, 0));
+					if(brown)
+						tmpCanvas.drawBorder(Rect(Point(0, 0), tmpImg->dimensions()), Colors::WHITE, 1);
+					genSlider(tmpImg, canvas);
+					return newImg;
+				}
+			}
+		};
+
+		layout[0].push_back(ImageLocator(spriteName, EImageBlitMode::SIMPLE));
+	}
+
+	return layout;
+}
+
 AssetGenerator::CanvasPtr AssetGenerator::createCreatureInfoPanel(int boxesAmount) const
 {
 	Point size(438, 187);

+ 1 - 0
client/render/AssetGenerator.h

@@ -36,6 +36,7 @@ public:
 	void addAnimationFile(const AnimationPath & path, AnimationLayoutMap & anim);
 
 	AnimationLayoutMap createAdventureMapButton(const ImagePath & overlay, bool small);
+	AnimationLayoutMap createSliderBar(bool brown, bool horizontal, int length);
 
 private:
 	struct PaletteAnimation

+ 55 - 17
client/widgets/Slider.cpp

@@ -16,8 +16,12 @@
 #include "../gui/MouseButton.h"
 #include "../gui/Shortcut.h"
 #include "../GameEngine.h"
+#include "../render/AssetGenerator.h"
 #include "../render/Canvas.h"
 #include "../render/Colors.h"
+#include "../render/IRenderHandler.h"
+
+#include "../../lib/CConfigHandler.h"
 
 void CSlider::mouseDragged(const Point & cursorPosition, const Point & lastUpdateDistance)
 {
@@ -31,15 +35,15 @@ void CSlider::mouseDragged(const Point & cursorPosition, const Point & lastUpdat
 	double newPosition = 0;
 	if(getOrientation() == Orientation::HORIZONTAL)
 	{
-		newPosition = cursorPosition.x - pos.x - 24;
+		newPosition = cursorPosition.x - pos.x - 16 - (barLength / 2);
 		newPosition *= positions;
-		newPosition /= (pos.w - 48);
+		newPosition /= (pos.w - 32 - barLength);
 	}
 	else
 	{
-		newPosition = cursorPosition.y - pos.y - 24;
+		newPosition = cursorPosition.y - pos.y - 16 - (barLength / 2);
 		newPosition *= positions;
-		newPosition /= (pos.h - 48);
+		newPosition /= (pos.h - 32 - barLength);
 	}
 
 	int positionInteger = std::round(newPosition);
@@ -99,7 +103,7 @@ void CSlider::updateSliderPos()
 		if(positions)
 		{
 			double part = static_cast<double>(value) / positions;
-			part*=(pos.w-48);
+			part*=(pos.w-32-barLength);
 			int newPos = static_cast<int>(part + pos.x + 16 - slider->pos.x);
 			slider->moveBy(Point(newPos, 0));
 		}
@@ -111,7 +115,7 @@ void CSlider::updateSliderPos()
 		if(positions)
 		{
 			double part = static_cast<double>(value) / positions;
-			part*=(pos.h-48);
+			part*=(pos.h-32-barLength);
 			int newPos = static_cast<int>(part + pos.y + 16 - slider->pos.y);
 			slider->moveBy(Point(0, newPos));
 		}
@@ -140,17 +144,21 @@ double CSlider::getClickPos(const Point & cursorPosition)
 {
 	double pw = 0;
 	double rw = 0;
+	bool inside = cursorPosition.x >= pos.x && cursorPosition.x <= pos.x + pos.w && cursorPosition.y >= pos.y && cursorPosition.y <= pos.y + pos.h;
 	if(getOrientation() == Orientation::HORIZONTAL)
 	{
-		pw = cursorPosition.x-pos.x-25;
-		rw = pw / static_cast<double>(pos.w - 48);
+		pw = cursorPosition.x-pos.x-16-(barLength/2);
+		rw = pw / static_cast<double>(pos.w - 32 - barLength);
 	}
 	else
 	{
-		pw = cursorPosition.y-pos.y-24;
-		rw = pw / (pos.h-48);
+		pw = cursorPosition.y-pos.y-16-(barLength/2);
+		rw = pw / (pos.h - 32 - barLength);
 	}
 
+	if (inside)
+		rw = std::clamp(rw, 0.0, 1.0);
+
 	return rw;
 }
 
@@ -196,12 +204,14 @@ bool CSlider::receiveEvent(const Point &position, int eventType) const
 	return testTarget.isInside(position);
 }
 
-CSlider::CSlider(Point position, int totalw, const SliderMovingFunctor & Moved, int Capacity, int Amount, int Value, Orientation orientation, CSlider::EStyle style)
+CSlider::CSlider(Point position, int totalw, const SliderMovingFunctor & Moved, int Capacity, int Amount, int Value, Orientation orientation, CSlider::EStyle Style)
 	: Scrollable(LCLICK | DRAG, position, orientation ),
 	capacity(Capacity),
 	amount(Amount),
 	value(Value),
-	moved(Moved)
+	moved(Moved),
+	length(totalw),
+	style(Style)
 {
 	OBJECT_CONSTRUCTION;
 	setAmount(amount);
@@ -215,23 +225,20 @@ CSlider::CSlider(Point position, int totalw, const SliderMovingFunctor & Moved,
 
 		left = std::make_shared<CButton>(Point(), name, CButton::tooltip());
 		right = std::make_shared<CButton>(Point(), name, CButton::tooltip());
-		slider = std::make_shared<CButton>(Point(), name, CButton::tooltip());
 
 		left->setImageOrder(0, 1, 1, 1);
 		right->setImageOrder(2, 3, 3, 3);
-		slider->setImageOrder(4, 4, 4, 4);
 	}
 	else
 	{
 		left = std::make_shared<CButton>(Point(), AnimationPath::builtin(getOrientation() == Orientation::HORIZONTAL ? "SCNRBLF.DEF" : "SCNRBUP.DEF"), CButton::tooltip());
 		right = std::make_shared<CButton>(Point(), AnimationPath::builtin(getOrientation() == Orientation::HORIZONTAL ? "SCNRBRT.DEF" : "SCNRBDN.DEF"), CButton::tooltip());
-		slider = std::make_shared<CButton>(Point(), AnimationPath::builtin("SCNRBSL.DEF"), CButton::tooltip());
 	}
-	slider->setActOnDown(true);
-	slider->setSoundDisabled(true);
 	left->setSoundDisabled(true);
 	right->setSoundDisabled(true);
 
+	updateSlider();
+
 	if (getOrientation() == Orientation::HORIZONTAL)
 		right->moveBy(Point(totalw - right->pos.w, 0));
 	else
@@ -267,11 +274,42 @@ void CSlider::block( bool on )
 	slider->block(on);
 }
 
+void CSlider::updateSlider()
+{
+	OBJECT_CONSTRUCTION;
+
+	auto assetGenerator = ENGINE->renderHandler().getAssetGenerator();
+	auto layout = assetGenerator->createSliderBar(style == BROWN, getOrientation() == Orientation::HORIZONTAL, barLength);
+	std::string sliderName = "Slider-" + std::string(style == BROWN ? "brown" : "blue") + "-" + std::string(getOrientation() == Orientation::HORIZONTAL ? "horizontal" : "vertical") + "-" + std::to_string(barLength);
+	assetGenerator->addAnimationFile(AnimationPath::builtin("SPRITES/" + sliderName), layout);
+	ENGINE->renderHandler().updateGeneratedAssets();
+
+	slider = std::make_shared<CButton>(Point(), AnimationPath::builtin(sliderName), CButton::tooltip());
+	slider->setActOnDown(true);
+	slider->setSoundDisabled(true);
+}
+
 void CSlider::setAmount( int to )
 {
 	amount = to;
 	positions = to - capacity;
 	vstd::amax(positions, 0);
+
+	if(settings["general"]["enableUiEnhancements"].Bool())
+	{
+		int track = length - 32;
+		if(to > 0)
+			barLength = (track * capacity) / to;
+		else
+			barLength = track;
+		vstd::amax(barLength, 16);
+		vstd::amin(barLength, track);
+	}
+	else
+		barLength = 16;
+
+	updateSlider();
+	updateSliderPos();
 }
 
 void CSlider::showAll(Canvas & to)

+ 15 - 6
client/widgets/Slider.h

@@ -18,6 +18,14 @@ class CButton;
 /// A typical slider which can be orientated horizontally/vertically.
 class CSlider : public Scrollable
 {
+public:
+	enum EStyle
+	{
+		BROWN,
+		BLUE
+	};
+
+private:
 	//if vertical then left=up
 	std::shared_ptr<CButton> left;
 	std::shared_ptr<CButton> right;
@@ -33,20 +41,21 @@ class CSlider : public Scrollable
 	int amount;
 	/// topmost vislble (first active) element
 	int value;
+	/// length of slider
+	int length;
+	/// length of slider button
+	int barLength;
+	/// color of slider
+	EStyle style;
 
 	CFunctionList<void(int)> moved;
 
 	void updateSliderPos();
+	void updateSlider();
 
 	double getClickPos(const Point & cursorPosition);
 
 public:
-	enum EStyle
-	{
-		BROWN,
-		BLUE
-	};
-
 	void block(bool on);
 
 	/// If set, mouse scroll will only scroll slider when inside of this area

+ 1 - 1
client/windows/GUIClasses.cpp

@@ -1638,7 +1638,7 @@ void CObjectListWindow::init(std::shared_ptr<CIntObject> titleWidget_, std::stri
 {
 	titleWidget = titleWidget_;
 
-	title = std::make_shared<CLabel>(152, 27, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, _title);
+	title = std::make_shared<CLabel>(152, titleWidget_ ? 27 : 51, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, _title);
 	descr = std::make_shared<CLabel>(145, 133, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, _descr);
 	exit = std::make_shared<CButton>( Point(228, 402), AnimationPath::builtin(blue ? "MuBcanc" : "ICANCEL.DEF"), CButton::tooltip(), std::bind(&CObjectListWindow::exitPressed, this), EShortcut::GLOBAL_CANCEL);