Browse Source

Moved slider class to a separate file

Ivan Savenko 2 năm trước cách đây
mục cha
commit
6fe00ad55c

+ 4 - 0
client/CMakeLists.txt

@@ -99,6 +99,8 @@ set(client_SRCS
 	widgets/MiscWidgets.cpp
 	widgets/ObjectLists.cpp
 	widgets/TextControls.cpp
+	widgets/Scrollable.cpp
+	widgets/Slider.cpp
 	widgets/CArtifactsOfHeroBase.cpp
 	widgets/CArtifactsOfHeroMain.cpp
 	widgets/CArtifactsOfHeroKingdom.cpp
@@ -251,6 +253,8 @@ set(client_HEADERS
 	widgets/MiscWidgets.h
 	widgets/ObjectLists.h
 	widgets/TextControls.h
+	widgets/Scrollable.h
+	widgets/Slider.h
 	widgets/CArtifactsOfHeroBase.h
 	widgets/CArtifactsOfHeroMain.h
 	widgets/CArtifactsOfHeroKingdom.h

+ 1 - 0
client/gui/InterfaceObjectConfigurable.cpp

@@ -21,6 +21,7 @@
 #include "../widgets/Buttons.h"
 #include "../widgets/MiscWidgets.h"
 #include "../widgets/ObjectLists.h"
+#include "../widgets/Slider.h"
 #include "../widgets/TextControls.h"
 #include "../windows/GUIClasses.h"
 #include "../windows/InfoWindows.h"

+ 2 - 1
client/lobby/CSelectionBase.cpp

@@ -28,10 +28,11 @@
 #include "../gui/Shortcut.h"
 #include "../gui/WindowHandler.h"
 #include "../mainmenu/CMainMenu.h"
-#include "../widgets/CComponent.h"
 #include "../widgets/Buttons.h"
+#include "../widgets/CComponent.h"
 #include "../widgets/MiscWidgets.h"
 #include "../widgets/ObjectLists.h"
+#include "../widgets/Slider.h"
 #include "../widgets/TextControls.h"
 #include "../windows/GUIClasses.h"
 #include "../windows/InfoWindows.h"

+ 1 - 0
client/lobby/OptionsTab.cpp

@@ -20,6 +20,7 @@
 #include "../widgets/Buttons.h"
 #include "../widgets/MiscWidgets.h"
 #include "../widgets/ObjectLists.h"
+#include "../widgets/Slider.h"
 #include "../widgets/TextControls.h"
 #include "../windows/GUIClasses.h"
 #include "../windows/InfoWindows.h"

+ 1 - 0
client/lobby/RandomMapTab.cpp

@@ -21,6 +21,7 @@
 #include "../widgets/Buttons.h"
 #include "../widgets/MiscWidgets.h"
 #include "../widgets/ObjectLists.h"
+#include "../widgets/Slider.h"
 #include "../widgets/TextControls.h"
 #include "../windows/GUIClasses.h"
 #include "../windows/InfoWindows.h"

+ 1 - 0
client/lobby/SelectionTab.cpp

@@ -22,6 +22,7 @@
 #include "../widgets/Buttons.h"
 #include "../widgets/MiscWidgets.h"
 #include "../widgets/ObjectLists.h"
+#include "../widgets/Slider.h"
 #include "../widgets/TextControls.h"
 #include "../windows/GUIClasses.h"
 #include "../windows/InfoWindows.h"

+ 0 - 340
client/widgets/Buttons.cpp

@@ -472,343 +472,3 @@ int CToggleGroup::getSelected() const
 {
 	return selectedID;
 }
-
-void CSlider::sliderClicked()
-{
-	addUsedEvents(MOVE);
-}
-
-void CSlider::mouseMoved (const Point & cursorPosition)
-{
-	double v = 0;
-	if(horizontal)
-	{
-		if(	std::abs(cursorPosition.y-(pos.y+pos.h/2)) > pos.h/2+40  ||  std::abs(cursorPosition.x-(pos.x+pos.w/2)) > pos.w/2  )
-			return;
-		v = cursorPosition.x - pos.x - 24;
-		v *= positions;
-		v /= (pos.w - 48);
-	}
-	else
-	{
-		if(std::abs(cursorPosition.x-(pos.x+pos.w/2)) > pos.w/2+40  ||  std::abs(cursorPosition.y-(pos.y+pos.h/2)) > pos.h/2  )
-			return;
-		v = cursorPosition.y - pos.y - 24;
-		v *= positions;
-		v /= (pos.h - 48);
-	}
-	v += 0.5;
-	if(v!=value)
-	{
-		moveTo(static_cast<int>(v));
-	}
-}
-
-void CSlider::setScrollStep(int to)
-{
-	scrollStep = to;
-}
-
-void CSlider::setScrollBounds(const Rect & bounds )
-{
-	scrollBounds = bounds;
-}
-
-void CSlider::clearScrollBounds()
-{
-	scrollBounds = std::nullopt;
-}
-
-int CSlider::getAmount() const
-{
-	return amount;
-}
-
-int CSlider::getValue() const
-{
-	return value;
-}
-
-int CSlider::getCapacity() const
-{
-	return capacity;
-}
-
-void CSlider::moveLeft()
-{
-	moveTo(value-1);
-}
-
-void CSlider::moveRight()
-{
-	moveTo(value+1);
-}
-
-void CSlider::moveBy(int amount)
-{
-	moveTo(value + amount);
-}
-
-void CSlider::updateSliderPos()
-{
-	if(horizontal)
-	{
-		if(positions)
-		{
-			double part = static_cast<double>(value) / positions;
-			part*=(pos.w-48);
-			int newPos = static_cast<int>(part + pos.x + 16 - slider->pos.x);
-			slider->moveBy(Point(newPos, 0));
-		}
-		else
-			slider->moveTo(Point(pos.x+16, pos.y));
-	}
-	else
-	{
-		if(positions)
-		{
-			double part = static_cast<double>(value) / positions;
-			part*=(pos.h-48);
-			int newPos = static_cast<int>(part + pos.y + 16 - slider->pos.y);
-			slider->moveBy(Point(0, newPos));
-		}
-		else
-			slider->moveTo(Point(pos.x, pos.y+16));
-	}
-}
-
-void CSlider::moveTo(int to)
-{
-	vstd::amax(to, 0);
-	vstd::amin(to, positions);
-
-	//same, old position?
-	if(value == to)
-		return;
-	value = to;
-
-	updateSliderPos();
-
-	moved(to);
-}
-
-void CSlider::clickLeft(tribool down, bool previousState)
-{
-	if(down && !slider->isBlocked())
-	{
-		double pw = 0;
-		double rw = 0;
-		if(horizontal)
-		{
-			pw = GH.getCursorPosition().x-pos.x-25;
-			rw = pw / static_cast<double>(pos.w - 48);
-		}
-		else
-		{
-			pw = GH.getCursorPosition().y-pos.y-24;
-			rw = pw / (pos.h-48);
-		}
-		if(pw < -8  ||  pw > (horizontal ? pos.w : pos.h) - 40)
-			return;
-		// 		if (rw>1) return;
-		// 		if (rw<0) return;
-		slider->clickLeft(true, slider->isMouseButtonPressed(MouseButton::LEFT));
-		moveTo((int)(rw * positions  +  0.5));
-		return;
-	}
-	removeUsedEvents(MOVE);
-}
-
-bool CSlider::receiveEvent(const Point &position, int eventType) const
-{
-	if (eventType != WHEEL && eventType != GESTURE_PANNING)
-	{
-		return CIntObject::receiveEvent(position, eventType);
-	}
-
-	if (!scrollBounds)
-		return true;
-
-	Rect testTarget = *scrollBounds + pos.topLeft();
-
-	return testTarget.isInside(position);
-}
-
-void CSlider::setPanningStep(int to)
-{
-	panningDistanceSingle = to;
-}
-
-void CSlider::panning(bool on)
-{
-	panningDistanceAccumulated = 0;
-}
-
-void CSlider::gesturePanning(const Point & distanceDelta)
-{
-	if (horizontal)
-		panningDistanceAccumulated += -distanceDelta.x;
-	else
-		panningDistanceAccumulated += distanceDelta.y;
-
-	if (-panningDistanceAccumulated > panningDistanceSingle )
-	{
-		int scrollAmount = (-panningDistanceAccumulated) / panningDistanceSingle;
-		moveBy(-scrollAmount);
-		panningDistanceAccumulated += scrollAmount * panningDistanceSingle;
-	}
-
-	if (panningDistanceAccumulated > panningDistanceSingle )
-	{
-		int scrollAmount = panningDistanceAccumulated / panningDistanceSingle;
-		moveBy(scrollAmount);
-		panningDistanceAccumulated += -scrollAmount * panningDistanceSingle;
-	}
-}
-
-CSlider::CSlider(Point position, int totalw, std::function<void(int)> Moved, int Capacity, int Amount, int Value, bool Horizontal, CSlider::EStyle style)
-	: CIntObject(LCLICK | RCLICK | WHEEL | GESTURE_PANNING ),
-	capacity(Capacity),
-	horizontal(Horizontal),
-	amount(Amount),
-	value(Value),
-	scrollStep(1),
-	moved(Moved),
-	panningDistanceAccumulated(0),
-	panningDistanceSingle(32)
-{
-	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
-	setAmount(amount);
-	vstd::amax(value, 0);
-	vstd::amin(value, positions);
-
-	pos.x += position.x;
-	pos.y += position.y;
-
-	if(style == BROWN)
-	{
-		std::string name = horizontal ? "IGPCRDIV.DEF" : "OVBUTN2.DEF";
-		//NOTE: this images do not have "blocked" frames. They should be implemented somehow (e.g. palette transform or something...)
-
-		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(), horizontal ? "SCNRBLF.DEF" : "SCNRBUP.DEF", CButton::tooltip());
-		right = std::make_shared<CButton>(Point(), horizontal ? "SCNRBRT.DEF" : "SCNRBDN.DEF", CButton::tooltip());
-		slider = std::make_shared<CButton>(Point(), "SCNRBSL.DEF", CButton::tooltip());
-	}
-	slider->actOnDown = true;
-	slider->soundDisabled = true;
-	left->soundDisabled = true;
-	right->soundDisabled = true;
-
-	if (horizontal)
-		right->moveBy(Point(totalw - right->pos.w, 0));
-	else
-		right->moveBy(Point(0, totalw - right->pos.h));
-
-	left->addCallback(std::bind(&CSlider::moveLeft,this));
-	right->addCallback(std::bind(&CSlider::moveRight,this));
-	slider->addCallback(std::bind(&CSlider::sliderClicked,this));
-
-	if(horizontal)
-	{
-		pos.h = slider->pos.h;
-		pos.w = totalw;
-	}
-	else
-	{
-		pos.w = slider->pos.w;
-		pos.h = totalw;
-	}
-
-	updateSliderPos();
-}
-
-CSlider::~CSlider() = default;
-
-void CSlider::block( bool on )
-{
-	left->block(on);
-	right->block(on);
-	slider->block(on);
-}
-
-void CSlider::setAmount( int to )
-{
-	amount = to;
-	positions = to - capacity;
-	vstd::amax(positions, 0);
-}
-
-void CSlider::showAll(Canvas & to)
-{
-	to.drawColor(pos, Colors::BLACK);
-	CIntObject::showAll(to);
-}
-
-void CSlider::wheelScrolled(int distance)
-{
-	// vertical slider -> scrolling up move slider upwards
-	// horizontal slider -> scrolling up moves slider towards right
-	bool positive = ((distance < 0) != horizontal);
-
-	moveTo(value + 3 * (positive ? +scrollStep : -scrollStep));
-}
-
-void CSlider::keyPressed(EShortcut key)
-{
-	int moveDest = value;
-	switch(key)
-	{
-	case EShortcut::MOVE_UP:
-		if (!horizontal)
-			moveDest = value - scrollStep;
-		break;
-	case EShortcut::MOVE_LEFT:
-		if (horizontal)
-			moveDest = value - scrollStep;
-		break;
-	case EShortcut::MOVE_DOWN:
-		if (!horizontal)
-			moveDest = value + scrollStep;
-		break;
-	case EShortcut::MOVE_RIGHT:
-		if (horizontal)
-			moveDest = value + scrollStep;
-		break;
-	case EShortcut::MOVE_PAGE_UP:
-		moveDest = value - capacity + scrollStep;
-		break;
-	case EShortcut::MOVE_PAGE_DOWN:
-		moveDest = value + capacity - scrollStep;
-		break;
-	case EShortcut::MOVE_FIRST:
-		moveDest = 0;
-		break;
-	case EShortcut::MOVE_LAST:
-		moveDest = amount - capacity;
-		break;
-	default:
-		return;
-	}
-
-	moveTo(moveDest);
-}
-
-void CSlider::moveToMin()
-{
-	moveTo(0);
-}
-
-void CSlider::moveToMax()
-{
-	moveTo(amount);
-}

+ 0 - 91
client/widgets/Buttons.h

@@ -181,94 +181,3 @@ public:
 	void setSelectedOnly(int id);
 	int getSelected() const;
 };
-
-/// A typical slider which can be orientated horizontally/vertically.
-class CSlider : public CIntObject
-{
-	//if vertical then left=up
-	std::shared_ptr<CButton> left;
-	std::shared_ptr<CButton> right;
-	std::shared_ptr<CButton> slider;
-
-	std::optional<Rect> scrollBounds;
-
-	/// how many elements are visible simultaneously
-	int capacity;
-	/// number of highest position, or 0 if there is only one
-	int positions;
-	/// if true, then slider is not vertical but horizontal
-	bool horizontal;
-	/// total amount of elements in the list
-	int amount;
-	/// topmost vislble (first active) element
-	int value;
-	/// how many elements will be scrolled via one click, default = 1
-	int scrollStep;
-
-	/// How far player must move finger/mouse to move slider by 1 via gesture
-	int panningDistanceSingle;
-	/// How far have player moved finger/mouse via gesture so far.
-	int panningDistanceAccumulated;
-
-	CFunctionList<void(int)> moved;
-
-	void updateSliderPos();
-	void sliderClicked();
-
-public:
-	enum EStyle
-	{
-		BROWN,
-		BLUE
-	};
-
-	void block(bool on);
-
-	/// Controls how many items wil be scrolled via one click
-	void setScrollStep(int to);
-
-	/// Controls size of panning step needed to move list by 1 item
-	void setPanningStep(int to);
-
-	/// If set, mouse scroll will only scroll slider when inside of this area
-	void setScrollBounds(const Rect & bounds );
-	void clearScrollBounds();
-
-	/// Value modifiers
-	void moveLeft();
-	void moveRight();
-	void moveTo(int value);
-	void moveBy(int amount);
-	void moveToMin();
-	void moveToMax();
-
-	/// Amount modifier
-	void setAmount(int to);
-
-	/// Accessors
-	int getAmount() const;
-	int getValue() const;
-	int getCapacity() const;
-
-	void addCallback(std::function<void(int)> callback);
-
-	bool receiveEvent(const Point & position, int eventType) const override;
-
-	void keyPressed(EShortcut key) override;
-	void wheelScrolled(int distance) override;
-	void gesturePanning(const Point & distanceDelta) override;
-	void clickLeft(tribool down, bool previousState) override;
-	void mouseMoved (const Point & cursorPosition) override;
-	void showAll(Canvas & to) override;
-	void panning(bool on) override;
-
-	 /// @param position coordinates of slider
-	 /// @param length length of slider ribbon, including left/right buttons
-	 /// @param Moved function that will be called whenever slider moves
-	 /// @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,
-		int Value=0, bool Horizontal=true, EStyle style = BROWN);
-	~CSlider();
-};

+ 1 - 1
client/widgets/ObjectLists.cpp

@@ -11,7 +11,7 @@
 #include "ObjectLists.h"
 
 #include "../gui/CGuiHandler.h"
-#include "Buttons.h"
+#include "Slider.h"
 
 CObjectList::CObjectList(CreateFunc create)
 	: createObject(create)

+ 12 - 0
client/widgets/Scrollable.cpp

@@ -0,0 +1,12 @@
+/*
+ * Scrollable.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#include "StdInc.h"
+#include "Scrollable.h"

+ 13 - 0
client/widgets/Scrollable.h

@@ -0,0 +1,13 @@
+/*
+ * Scrollable.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+#include "../gui/CIntObject.h"

+ 359 - 0
client/widgets/Slider.cpp

@@ -0,0 +1,359 @@
+/*
+ * Slider.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#include "StdInc.h"
+#include "Slider.h"
+
+#include "Buttons.h"
+
+#include "../gui/MouseButton.h"
+#include "../gui/Shortcut.h"
+#include "../gui/CGuiHandler.h"
+#include "../render/Canvas.h"
+
+void CSlider::sliderClicked()
+{
+	addUsedEvents(MOVE);
+}
+
+void CSlider::mouseMoved (const Point & cursorPosition)
+{
+	double v = 0;
+	if(horizontal)
+	{
+		if(	std::abs(cursorPosition.y-(pos.y+pos.h/2)) > pos.h/2+40  ||  std::abs(cursorPosition.x-(pos.x+pos.w/2)) > pos.w/2  )
+			return;
+		v = cursorPosition.x - pos.x - 24;
+		v *= positions;
+		v /= (pos.w - 48);
+	}
+	else
+	{
+		if(std::abs(cursorPosition.x-(pos.x+pos.w/2)) > pos.w/2+40  ||  std::abs(cursorPosition.y-(pos.y+pos.h/2)) > pos.h/2  )
+			return;
+		v = cursorPosition.y - pos.y - 24;
+		v *= positions;
+		v /= (pos.h - 48);
+	}
+	v += 0.5;
+	if(v!=value)
+	{
+		moveTo(static_cast<int>(v));
+	}
+}
+
+void CSlider::setScrollStep(int to)
+{
+	scrollStep = to;
+}
+
+void CSlider::setScrollBounds(const Rect & bounds )
+{
+	scrollBounds = bounds;
+}
+
+void CSlider::clearScrollBounds()
+{
+	scrollBounds = std::nullopt;
+}
+
+int CSlider::getAmount() const
+{
+	return amount;
+}
+
+int CSlider::getValue() const
+{
+	return value;
+}
+
+int CSlider::getCapacity() const
+{
+	return capacity;
+}
+
+void CSlider::moveLeft()
+{
+	moveTo(value-1);
+}
+
+void CSlider::moveRight()
+{
+	moveTo(value+1);
+}
+
+void CSlider::moveBy(int amount)
+{
+	moveTo(value + amount);
+}
+
+void CSlider::updateSliderPos()
+{
+	if(horizontal)
+	{
+		if(positions)
+		{
+			double part = static_cast<double>(value) / positions;
+			part*=(pos.w-48);
+			int newPos = static_cast<int>(part + pos.x + 16 - slider->pos.x);
+			slider->moveBy(Point(newPos, 0));
+		}
+		else
+			slider->moveTo(Point(pos.x+16, pos.y));
+	}
+	else
+	{
+		if(positions)
+		{
+			double part = static_cast<double>(value) / positions;
+			part*=(pos.h-48);
+			int newPos = static_cast<int>(part + pos.y + 16 - slider->pos.y);
+			slider->moveBy(Point(0, newPos));
+		}
+		else
+			slider->moveTo(Point(pos.x, pos.y+16));
+	}
+}
+
+void CSlider::moveTo(int to)
+{
+	vstd::amax(to, 0);
+	vstd::amin(to, positions);
+
+	//same, old position?
+	if(value == to)
+		return;
+	value = to;
+
+	updateSliderPos();
+
+	moved(to);
+}
+
+void CSlider::clickLeft(tribool down, bool previousState)
+{
+	if(down && !slider->isBlocked())
+	{
+		double pw = 0;
+		double rw = 0;
+		if(horizontal)
+		{
+			pw = GH.getCursorPosition().x-pos.x-25;
+			rw = pw / static_cast<double>(pos.w - 48);
+		}
+		else
+		{
+			pw = GH.getCursorPosition().y-pos.y-24;
+			rw = pw / (pos.h-48);
+		}
+		if(pw < -8  ||  pw > (horizontal ? pos.w : pos.h) - 40)
+			return;
+		// 		if (rw>1) return;
+		// 		if (rw<0) return;
+		slider->clickLeft(true, slider->isMouseButtonPressed(MouseButton::LEFT));
+		moveTo((int)(rw * positions  +  0.5));
+		return;
+	}
+	removeUsedEvents(MOVE);
+}
+
+bool CSlider::receiveEvent(const Point &position, int eventType) const
+{
+	if (eventType != WHEEL && eventType != GESTURE_PANNING)
+	{
+		return CIntObject::receiveEvent(position, eventType);
+	}
+
+	if (!scrollBounds)
+		return true;
+
+	Rect testTarget = *scrollBounds + pos.topLeft();
+
+	return testTarget.isInside(position);
+}
+
+void CSlider::setPanningStep(int to)
+{
+	panningDistanceSingle = to;
+}
+
+void CSlider::panning(bool on)
+{
+	panningDistanceAccumulated = 0;
+}
+
+void CSlider::gesturePanning(const Point & distanceDelta)
+{
+	if (horizontal)
+		panningDistanceAccumulated += -distanceDelta.x;
+	else
+		panningDistanceAccumulated += distanceDelta.y;
+
+	if (-panningDistanceAccumulated > panningDistanceSingle )
+	{
+		int scrollAmount = (-panningDistanceAccumulated) / panningDistanceSingle;
+		moveBy(-scrollAmount);
+		panningDistanceAccumulated += scrollAmount * panningDistanceSingle;
+	}
+
+	if (panningDistanceAccumulated > panningDistanceSingle )
+	{
+		int scrollAmount = panningDistanceAccumulated / panningDistanceSingle;
+		moveBy(scrollAmount);
+		panningDistanceAccumulated += -scrollAmount * panningDistanceSingle;
+	}
+}
+
+CSlider::CSlider(Point position, int totalw, std::function<void(int)> Moved, int Capacity, int Amount, int Value, bool Horizontal, CSlider::EStyle style)
+	: CIntObject(LCLICK | RCLICK | WHEEL | GESTURE_PANNING ),
+	capacity(Capacity),
+	horizontal(Horizontal),
+	amount(Amount),
+	value(Value),
+	scrollStep(1),
+	moved(Moved),
+	panningDistanceAccumulated(0),
+	panningDistanceSingle(32)
+{
+	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
+	setAmount(amount);
+	vstd::amax(value, 0);
+	vstd::amin(value, positions);
+
+	pos.x += position.x;
+	pos.y += position.y;
+
+	if(style == BROWN)
+	{
+		std::string name = horizontal ? "IGPCRDIV.DEF" : "OVBUTN2.DEF";
+		//NOTE: this images do not have "blocked" frames. They should be implemented somehow (e.g. palette transform or something...)
+
+		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(), horizontal ? "SCNRBLF.DEF" : "SCNRBUP.DEF", CButton::tooltip());
+		right = std::make_shared<CButton>(Point(), horizontal ? "SCNRBRT.DEF" : "SCNRBDN.DEF", CButton::tooltip());
+		slider = std::make_shared<CButton>(Point(), "SCNRBSL.DEF", CButton::tooltip());
+	}
+	slider->actOnDown = true;
+	slider->soundDisabled = true;
+	left->soundDisabled = true;
+	right->soundDisabled = true;
+
+	if (horizontal)
+		right->moveBy(Point(totalw - right->pos.w, 0));
+	else
+		right->moveBy(Point(0, totalw - right->pos.h));
+
+	left->addCallback(std::bind(&CSlider::moveLeft,this));
+	right->addCallback(std::bind(&CSlider::moveRight,this));
+	slider->addCallback(std::bind(&CSlider::sliderClicked,this));
+
+	if(horizontal)
+	{
+		pos.h = slider->pos.h;
+		pos.w = totalw;
+	}
+	else
+	{
+		pos.w = slider->pos.w;
+		pos.h = totalw;
+	}
+
+	updateSliderPos();
+}
+
+CSlider::~CSlider() = default;
+
+void CSlider::block( bool on )
+{
+	left->block(on);
+	right->block(on);
+	slider->block(on);
+}
+
+void CSlider::setAmount( int to )
+{
+	amount = to;
+	positions = to - capacity;
+	vstd::amax(positions, 0);
+}
+
+void CSlider::showAll(Canvas & to)
+{
+	to.drawColor(pos, Colors::BLACK);
+	CIntObject::showAll(to);
+}
+
+void CSlider::wheelScrolled(int distance)
+{
+	// vertical slider -> scrolling up move slider upwards
+	// horizontal slider -> scrolling up moves slider towards right
+	bool positive = ((distance < 0) != horizontal);
+
+	moveTo(value + 3 * (positive ? +scrollStep : -scrollStep));
+}
+
+void CSlider::keyPressed(EShortcut key)
+{
+	int moveDest = value;
+	switch(key)
+	{
+	case EShortcut::MOVE_UP:
+		if (!horizontal)
+			moveDest = value - scrollStep;
+		break;
+	case EShortcut::MOVE_LEFT:
+		if (horizontal)
+			moveDest = value - scrollStep;
+		break;
+	case EShortcut::MOVE_DOWN:
+		if (!horizontal)
+			moveDest = value + scrollStep;
+		break;
+	case EShortcut::MOVE_RIGHT:
+		if (horizontal)
+			moveDest = value + scrollStep;
+		break;
+	case EShortcut::MOVE_PAGE_UP:
+		moveDest = value - capacity + scrollStep;
+		break;
+	case EShortcut::MOVE_PAGE_DOWN:
+		moveDest = value + capacity - scrollStep;
+		break;
+	case EShortcut::MOVE_FIRST:
+		moveDest = 0;
+		break;
+	case EShortcut::MOVE_LAST:
+		moveDest = amount - capacity;
+		break;
+	default:
+		return;
+	}
+
+	moveTo(moveDest);
+}
+
+void CSlider::moveToMin()
+{
+	moveTo(0);
+}
+
+void CSlider::moveToMax()
+{
+	moveTo(amount);
+}

+ 107 - 0
client/widgets/Slider.h

@@ -0,0 +1,107 @@
+/*
+ * Slider.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+#include "Scrollable.h"
+#include "../../lib/FunctionList.h"
+
+class CButton;
+
+/// A typical slider which can be orientated horizontally/vertically.
+class CSlider : public CIntObject
+{
+	//if vertical then left=up
+	std::shared_ptr<CButton> left;
+	std::shared_ptr<CButton> right;
+	std::shared_ptr<CButton> slider;
+
+	std::optional<Rect> scrollBounds;
+
+	/// how many elements are visible simultaneously
+	int capacity;
+	/// number of highest position, or 0 if there is only one
+	int positions;
+	/// if true, then slider is not vertical but horizontal
+	bool horizontal;
+	/// total amount of elements in the list
+	int amount;
+	/// topmost vislble (first active) element
+	int value;
+	/// how many elements will be scrolled via one click, default = 1
+	int scrollStep;
+
+	/// How far player must move finger/mouse to move slider by 1 via gesture
+	int panningDistanceSingle;
+	/// How far have player moved finger/mouse via gesture so far.
+	int panningDistanceAccumulated;
+
+	CFunctionList<void(int)> moved;
+
+	void updateSliderPos();
+	void sliderClicked();
+
+public:
+	enum EStyle
+	{
+		BROWN,
+		BLUE
+	};
+
+	void block(bool on);
+
+	/// Controls how many items wil be scrolled via one click
+	void setScrollStep(int to);
+
+	/// Controls size of panning step needed to move list by 1 item
+	void setPanningStep(int to);
+
+	/// If set, mouse scroll will only scroll slider when inside of this area
+	void setScrollBounds(const Rect & bounds );
+	void clearScrollBounds();
+
+	/// Value modifiers
+	void moveLeft();
+	void moveRight();
+	void moveTo(int value);
+	void moveBy(int amount);
+	void moveToMin();
+	void moveToMax();
+
+	/// Amount modifier
+	void setAmount(int to);
+
+	/// Accessors
+	int getAmount() const;
+	int getValue() const;
+	int getCapacity() const;
+
+	void addCallback(std::function<void(int)> callback);
+
+	bool receiveEvent(const Point & position, int eventType) const override;
+
+	void keyPressed(EShortcut key) override;
+	void wheelScrolled(int distance) override;
+	void gesturePanning(const Point & distanceDelta) override;
+	void clickLeft(tribool down, bool previousState) override;
+	void mouseMoved (const Point & cursorPosition) override;
+	void showAll(Canvas & to) override;
+	void panning(bool on) override;
+
+	 /// @param position coordinates of slider
+	 /// @param length length of slider ribbon, including left/right buttons
+	 /// @param Moved function that will be called whenever slider moves
+	 /// @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,
+		int Value=0, bool Horizontal=true, EStyle style = BROWN);
+	~CSlider();
+};

+ 1 - 1
client/widgets/TextControls.cpp

@@ -10,7 +10,7 @@
 #include "StdInc.h"
 #include "TextControls.h"
 
-#include "Buttons.h"
+#include "Slider.h"
 #include "Images.h"
 
 #include "../CPlayerInterface.h"

+ 1 - 0
client/windows/CMessage.cpp

@@ -18,6 +18,7 @@
 #include "../windows/InfoWindows.h"
 #include "../widgets/Buttons.h"
 #include "../widgets/CComponent.h"
+#include "../widgets/Slider.h"
 #include "../widgets/TextControls.h"
 #include "../gui/CGuiHandler.h"
 #include "../render/CAnimation.h"

+ 2 - 1
client/windows/CQuestLog.cpp

@@ -15,9 +15,10 @@
 
 #include "../gui/CGuiHandler.h"
 #include "../gui/Shortcut.h"
+#include "../widgets/Buttons.h"
 #include "../widgets/CComponent.h"
+#include "../widgets/Slider.h"
 #include "../adventureMap/AdventureMapInterface.h"
-#include "../widgets/Buttons.h"
 #include "../adventureMap/CMinimap.h"
 #include "../render/Canvas.h"
 #include "../renderSDL/SDL_Extensions.h"

+ 1 - 0
client/windows/CTradeWindow.cpp

@@ -18,6 +18,7 @@
 #include "../gui/Shortcut.h"
 #include "../gui/WindowHandler.h"
 #include "../widgets/Buttons.h"
+#include "../widgets/Slider.h"
 #include "../widgets/TextControls.h"
 #include "../windows/InfoWindows.h"
 

+ 1 - 0
client/windows/CreaturePurchaseCard.cpp

@@ -19,6 +19,7 @@
 #include "../gui/TextAlignment.h"
 #include "../gui/WindowHandler.h"
 #include "../widgets/Buttons.h"
+#include "../widgets/Slider.h"
 #include "../widgets/TextControls.h"
 #include "../widgets/CreatureCostBox.h"
 

+ 1 - 1
client/windows/GUIClasses.cpp

@@ -21,7 +21,6 @@
 #include "../CVideoHandler.h"
 #include "../CServerHandler.h"
 
-//#include "../adventureMap/CResDataBar.h"
 #include "../battle/BattleInterfaceClasses.h"
 #include "../battle/BattleInterface.h"
 
@@ -35,6 +34,7 @@
 #include "../widgets/MiscWidgets.h"
 #include "../widgets/CreatureCostBox.h"
 #include "../widgets/Buttons.h"
+#include "../widgets/Slider.h"
 #include "../widgets/TextControls.h"
 #include "../widgets/ObjectLists.h"
 

+ 1 - 0
client/windows/QuickRecruitmentWindow.cpp

@@ -13,6 +13,7 @@
 #include "../CPlayerInterface.h"
 #include "../widgets/Buttons.h"
 #include "../widgets/CreatureCostBox.h"
+#include "../widgets/Slider.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/Shortcut.h"
 #include "../../CCallback.h"

+ 1 - 0
client/windows/settings/GeneralOptionsTab.cpp

@@ -16,6 +16,7 @@
 #include "../../gui/CGuiHandler.h"
 #include "../../gui/WindowHandler.h"
 #include "../../widgets/Buttons.h"
+#include "../../widgets/Slider.h"
 #include "../../widgets/TextControls.h"
 #include "../../widgets/Images.h"
 #include "CGameInfo.h"