Last 3 elements: static text, list box and slider.

    Static text is just element with text, nothing special. I add only draw limit - for making animation like this

    List box

    Simple list box, add item, remove item, select one item, or multiple items.

    
    class slGUIListBoxTextDrawCallback : public slGUIDrawTextCallback
    {
    	friend class slGUIListBox;
    	slGUIListBox* m_lb = 0;
    	slGUIFont* m_defaultFont = 0;
    	slGUIFont* m_currFont = 0;
    public:
    	slGUIListBoxTextDrawCallback();
    	virtual ~slGUIListBoxTextDrawCallback() {}
    
    	virtual slGUIFont* OnFont(uint32_t r, char32_t) override;
    	virtual slColor* OnColor(uint32_t r, char32_t) override;
    };
    
    struct slGUIListBoxItem
    {
    	slString m_text;
    	uint32_t m_id = 0;
    	void* m_data = 0;
    
    	bool m_isSelected = false;
    };
    
    class slGUIListBox : public slGUIElement
    {
    	slGUIListBoxTextDrawCallback m_listBoxTextDrawCallback;
    	slArray<slGUIListBoxItem*> m_items;
    	float m_lineHeight = 0.f;
    	size_t m_firstItemIndexForDraw = 0;
    	size_t m_numberOfVisibleLines = 0;
    
    public:
    	slGUIListBox(slGUIWindow*, const slVec2f& position, const slVec2f& size);
    	virtual ~slGUIListBox();
    
    	virtual void Rebuild() final;
    	virtual void Update() final;
    	virtual void Draw(slGS* gs, float dt) final;	
    	
    	virtual void UpdateContentSize() final;
    
    	slGUIListBoxItem* AddItem(const char32_t*, uint32_t id, void* data);
    	void RemoveItem(slGUIListBoxItem*);
    
    	slArray<slGUIListBoxItem*>* GetItems() { return &m_items; }
    
    	bool m_drawItemBG = true;
    	bool m_multiSelect = false;
    
    	// return true if need to select
    	virtual bool OnSelect(slGUIListBoxItem*) { return true; }
    	// return true if need to deselect
    	virtual bool OnDeSelect(slGUIListBoxItem*) { return true; }
    };
    

    Implementation

    
    slGUIListBox::slGUIListBox(slGUIWindow* w, const slVec2f& position, const slVec2f& size)
    	:
    	slGUIElement::slGUIElement(w, position, size)
    {
    	m_flags |= slGUICommon::flag_wheelScroll;
    	m_listBoxTextDrawCallback.m_lb = this;
    	m_textDrawCallback = dynamic_cast<slGUIDrawTextCallback*>(&m_listBoxTextDrawCallback);
    }
    
    slGUIListBox::~slGUIListBox()
    {
    	for (size_t i = 0; i < m_items.m_size; ++i)
    	{
    		slDestroy(m_items.m_data[i]);
    	}
    }
    
    void slGUIListBox::Rebuild()
    {
    	slGUIElement::Rebuild();
    	m_lineHeight = 0.f;
    	const char32_t b[] = { U"X#$@WHM_" };
    	for (int i = 0; i < 9; ++i)
    	{
    		slGUIFont* font = m_textDrawCallback->OnFont(0, b[i]);
    		if (font->GetMaxSize().y > m_lineHeight)
    			m_lineHeight = (float)font->GetMaxSize().y;
    	}
    	m_numberOfVisibleLines = 0;
    	if (m_lineHeight && (m_buildRect.w - m_buildRect.y))
    	{
    		m_numberOfVisibleLines = (size_t)ceilf((m_buildRect.w - m_buildRect.y) / m_lineHeight);
    	}
    }
    
    void slGUIListBox::UpdateContentSize()
    {
    	m_contentSize.x = m_baseRect.z - m_baseRect.x;
    	m_contentSize.y = 0.f;
    
    	if (m_items.m_size)
    	{
    		m_contentSize.y = (float)(m_items.m_size)*m_lineHeight;
    
    		// + when scroll to bottom there is must 1 line be visible
    		m_contentSize.y += (m_baseRect.w - m_baseRect.y) - m_lineHeight;
    	}
    
    	UpdateScrollLimit();
    }
    
    void slGUIListBox::Update()
    {
    	slGUIElement::Update();
    
    	// find m_firstItemIndexForDraw, use only m_v_scroll
    	slVec2f pos;
    	pos.x = m_buildRect.x;
    	pos.y = m_buildRect.y - 0.f;
    	pos.y += (m_lineHeight * (float)m_firstItemIndexForDraw);
    	uint32_t itemsSize = m_items.m_size + m_numberOfVisibleLines;
    
    	if (pos.y < (m_buildRect.y - m_lineHeight - m_lineHeight))
    	{
    		float d = (m_buildRect.y - m_lineHeight - m_lineHeight) - pos.y;
    		int dd = (int)ceilf(d / m_lineHeight);
    
    		m_firstItemIndexForDraw += dd;
    
    		uint32_t lastIndex = itemsSize - m_items.m_size;
    		if (m_numberOfVisibleLines > lastIndex)
    			m_numberOfVisibleLines = lastIndex;
    	}
    	else if (pos.y > (m_buildRect.y - m_lineHeight - m_lineHeight))
    	{
    		if (m_firstItemIndexForDraw)
    		{
    			float d = pos.y - (m_buildRect.y - m_lineHeight - m_lineHeight);
    			int dd = (int)ceilf(d / m_lineHeight);
    
    			if ((size_t)dd < m_firstItemIndexForDraw)
    				m_firstItemIndexForDraw -= dd;
    			else
    				m_firstItemIndexForDraw = 0;
    		}
    	}
    }
    
    void slGUIListBox::Draw(slGS* gs, float dt)
    {
    	if (IsEnabled())
    	{
    		gs->SetScissorRect(m_clipRect, 0);
    		if (IsDrawBG())
    			gs->DrawGUIRectangle(m_buildRect, m_style->m_staticTextBGColor, m_style->m_staticTextBGColor, 0, 0);
    	}
    
    	if (m_items.m_size)
    	{
    		uint32_t index = m_firstItemIndexForDraw;
    
    		slVec2f pos;
    		pos.x = m_buildRect.x;
    		pos.y = m_buildRect.y - m_scroll.y;
    		pos.y += (m_lineHeight * (float)index);
    		uint32_t itemsSize = m_items.m_size + m_numberOfVisibleLines;
    
    		if (pos.y < (m_baseRect.y - m_lineHeight - m_lineHeight))
    		{
    			++m_firstItemIndexForDraw;
    
    			uint32_t lastIndex = itemsSize - m_items.m_size;
    			if (m_numberOfVisibleLines > lastIndex)
    				m_numberOfVisibleLines = lastIndex;
    		}
    		else if (pos.y > (m_baseRect.y - m_lineHeight))
    		{
    			if (m_firstItemIndexForDraw)
    				--m_firstItemIndexForDraw;
    		}
    
    		for (uint32_t i = 0; i < m_numberOfVisibleLines + 4; ++i)
    		{
    			slVec4f r;
    			r.x = pos.x;
    			r.y = pos.y;
    			r.z = m_buildRect.z;
    			r.w = r.y + m_lineHeight;
    
    
    			slVec4f rClip = r;
    			if (rClip.y < m_clipRect.y)
    				rClip.y = m_clipRect.y;
    			if (m_drawItemBG)
    			{
    				slColor cc;
    				cc = m_style->m_listboxLine1BGColor;
    				if (index)
    				{
    					if ((index % 2) != 0)
    						cc = m_style->m_listboxLine2BGColor;
    				}
    				
    				if (m_items.m_data[i]->m_isSelected)
    					cc = m_style->m_listboxSelectedLineBGColor;
    
    				gs->DrawGUIRectangle(r, cc, cc, 0, 0);
    			}
    
    			if (m_items.m_data[i]->m_text.size())
    			{
    				gs->DrawGUIText(
    					m_items.m_data[i]->m_text.c_str(),
    					m_items.m_data[i]->m_text.size(),
    					pos,
    					m_textDrawCallback);
    			}
    			
    			if (slMath::pointInRect(slInput::GetData()->mousePosition, r))
    			{
    				if (slInput::IsLMBHit())
    				{
    					if (m_items.m_data[i]->m_isSelected)
    					{
    						if (OnDeSelect(m_items.m_data[i]))
    						{
    							m_items.m_data[i]->m_isSelected = false;
    						}
    					}
    					else
    					{
    						if (OnSelect(m_items.m_data[i]))
    						{
    							if (!m_multiSelect)
    							{
    								for (size_t i2 = 0; i2 < m_items.m_size; ++i2)
    								{
    									m_items.m_data[i2]->m_isSelected = false;
    								}
    							}
    
    							m_items.m_data[i]->m_isSelected = true;
    						}
    					}
    				}
    			}
    
    			pos.y = pos.y + m_lineHeight;
    
    			++index;
    			if (index >= itemsSize)
    				break;
    		}
    	}
    }
    
    slGUIListBoxItem* slGUIListBox::AddItem(const char32_t* t, uint32_t id, void* data)
    {
    	slGUIListBoxItem* newItem = slCreate<slGUIListBoxItem>();
    
    	newItem->m_text = t;
    	newItem->m_data = data;
    	newItem->m_id = id;
    
    	m_items.push_back(newItem);
    	return newItem;
    }
    
    void slGUIListBox::RemoveItem(slGUIListBoxItem* it)
    {
    	SL_ASSERT_ST(it);
    	m_items.erase_first(it);
    	slDestroy(it);
    }
    

    Slider

    
    class slGUISlider : public slGUIElement
    {
    	slGUISliderTextDrawCallback m_sliderTextDrawCallback;
    	float m_controlWidth = 10.f;
    	
    	double m_valueFloat = 0.0;
    	uint32_t m_valueUint = 0;
    	int32_t m_valueInt = 0;
    
    	double m_valueMinFloat = 0.0;
    	double m_valueMaxFloat = 100.0;
    
    	uint32_t m_valueMinUint = 0;
    	uint32_t m_valueMaxUint = 100;
    
    	int32_t m_valueMinInt = -100;
    	int32_t m_valueMaxInt = 100;
    
    	slVec4f m_axisRect;
    	slVec4f m_axisRectFill;
    	float m_axisHeight = 5.f;
    	
    	slVec4f m_controlRect;
    	bool m_isClicked = false;
    
    	float m_axisIndent = 15.f;
    
    	slString m_text;
    
    	void findAxisRectFill();
    public:
    	slGUISlider(slGUIWindow*, const slVec2f& position, const slVec2f& size);
    	virtual ~slGUISlider();
    	
    	virtual void Rebuild() final;
    	virtual void Update() final;
    	virtual void Draw(slGS* gs, float dt) final;	
    
    	double* GetFloat() { return &m_valueFloat; }
    	uint32_t* GetUint() { return &m_valueUint; }
    	int32_t* GetInt() { return &m_valueInt; }
    
    	enum class ValueType
    	{
    		Float,
    		Uint,
    		Int
    	};
    	ValueType m_valueType = ValueType::Float;
    
    	void SetMinMaxFloat(double mn, double mx) { m_valueMinFloat = mn; m_valueMaxFloat = mx; }
    	void SetMinMaxUint(uint32_t mn, uint32_t mx) { m_valueMinUint = mn; m_valueMaxUint = mx; }
    	void SetMinMaxInt(int32_t mn, int32_t mx) { m_valueMinInt = mn; m_valueMaxInt = mx; }
    
    	bool m_drawText = true;
    
    	// will call m_text.pop_back(); number of times
    	uint32_t m_truncateFloatByThisNumOfChars = 10;
    };
    

    Code

    
    slGUISlider::slGUISlider(slGUIWindow* w, const slVec2f& position, const slVec2f& size)
    	:
    	slGUIElement(w, position, size)
    {
    	m_sliderTextDrawCallback.m_slider = this;
    	m_textDrawCallback = dynamic_cast<slGUIDrawTextCallback*>(&m_sliderTextDrawCallback);
    	SetDrawBG(false);
    }
    
    slGUISlider::~slGUISlider(){}
    
    
    void slGUISlider::Update()
    {
    	slGUIElement::Update();
    	if (m_window->GetRootElement()->m_scrollDelta.y)
    		Rebuild();
    
    	if (!m_isClicked)
    	{
    		if (slMath::pointInRect(slInput::GetData()->mousePosition, m_controlRect))
    		{
    			if (slInput::IsLMBHit())
    				m_isClicked = true;
    		}
    	}
    	else
    	{
    		if (slInput::IsKeyHit(slInput::KEY_ESCAPE))
    			m_isClicked = false;
    		else if (slInput::IsLMBRelease())
    			m_isClicked = false;
    		else
    		{
    			m_valueFloat += (double)slInput::GetData()->mouseMoveDelta.x;
    			m_valueInt += (int32_t)slInput::GetData()->mouseMoveDelta.x;
    			m_valueUint += (uint32_t)slInput::GetData()->mouseMoveDelta.x;
    
    			if (m_valueFloat < m_valueMinFloat) m_valueFloat = m_valueMinFloat;
    			if (m_valueFloat > m_valueMaxFloat) m_valueFloat = m_valueMaxFloat;
    
    			if (m_valueInt < m_valueMinInt) m_valueInt = m_valueMinInt;
    			if (m_valueInt > m_valueMaxInt) m_valueInt = m_valueMaxInt;
    
    			if (m_valueUint < m_valueMinUint) m_valueUint = m_valueMinUint;
    			if (m_valueUint > m_valueMaxUint) m_valueUint = m_valueMaxUint;
    
    			findAxisRectFill();
    		}
    	}
    }
    
    
    void slGUISlider::Rebuild()
    {
    	slGUIElement::Rebuild();
    	
    	auto H = m_buildRect.w - m_buildRect.y;
    
    	m_axisRect.x = m_buildRect.x + m_axisIndent;
    	m_axisRect.z = m_buildRect.z - m_axisIndent;
    	
    	m_axisRect.y = m_buildRect.y + (H * 0.5f);
    	m_axisRect.y -= m_axisHeight * 0.5f;
    	m_axisRect.w = m_axisRect.y + m_axisHeight;
    
    	findAxisRectFill();
    }
    
    void slGUISlider::Draw(slGS* gs, float dt)
    {
    	gs->SetScissorRect(m_clipRect, 0);
    	
    	gs->DrawGUIRectangle(m_axisRect, m_style->m_sliderAxisEmtpyColor, m_style->m_sliderAxisEmtpyColor, 0, 0);
    	gs->DrawGUIRectangle(m_axisRectFill, m_style->m_sliderAxisFillColor, m_style->m_sliderAxisFillColor, 0, 0);
    	gs->DrawGUIRectangle(m_controlRect, m_style->m_sliderControlColor, m_style->m_sliderControlColor, 0, 0);
    
    	if (m_drawText)
    	{
    		slGUIElement* parent = dynamic_cast<slGUIElement*>(GetParent());
    		if (parent && m_text.size())
    		{
    			gs->SetScissorRect(parent->m_clipRect, 0);
    
    			slVec2f textPosition;
    			textPosition.x = m_buildRect.z;
    			textPosition.y = m_axisRect.y - m_axisHeight;
    
    			gs->DrawGUIText(
    				m_text.c_str(),
    				m_text.size(),
    				textPosition,
    				m_textDrawCallback);
    		}
    	}
    }
    
    void slGUISlider::findAxisRectFill()
    {
    	m_axisRectFill = m_axisRect;
    
    	auto axisWidth = m_axisRect.z - m_axisRect.x;
    
    	float D = 0.f;
    
    	m_text.clear();
    
    	switch (m_valueType)
    	{
    	case slGUISlider::ValueType::Float:
    	{
    		m_text.append(m_valueFloat);
    		for (uint32_t i = 0; i < m_truncateFloatByThisNumOfChars; ++i)
    		{
    			m_text.pop_back();
    		}
    		double len = m_valueMaxFloat - m_valueMinFloat;
    		if (len != 0.0)
    		{
    			double M = 1.f / len;
    			double V = abs(m_valueMinFloat - m_valueFloat);
    			D = V * M;
    		}
    	}break;
    	case slGUISlider::ValueType::Uint:
    	{
    		m_text.append(m_valueUint);
    		uint32_t len = m_valueMaxUint - m_valueMinUint;
    
    	}break;
    	case slGUISlider::ValueType::Int:
    	{
    		m_text.append(m_valueInt);
    		int32_t len = m_valueMaxInt - m_valueMinInt;
    
    	}break;
    	}
    	
    	m_axisRectFill.z = m_axisRectFill.x + (axisWidth * D);
    
    	m_controlRect.x = m_buildRect.x + m_axisIndent - (m_controlWidth *0.5f);
    	m_controlRect.y = m_buildRect.y;
    	m_controlRect.z = m_controlRect.x + m_controlWidth;
    	m_controlRect.w = m_buildRect.w;
    
    	m_controlRect.x += m_axisRectFill.z - m_axisRectFill.x;
    	m_controlRect.z += m_axisRectFill.z - m_axisRectFill.x;
    }
    

    Download