179 lines
6.8 KiB
C++
179 lines
6.8 KiB
C++
#ifndef NUMERIC_INPUT_HPP
|
|
#define NUMERIC_INPUT_HPP
|
|
|
|
#include <limits>
|
|
#include <regex>
|
|
#include <type_traits>
|
|
|
|
#include <basic_widgets/abstract/input.hpp>
|
|
#include <basic_widgets/button.hpp>
|
|
#include <basic_widgets/utils/math.hpp>
|
|
|
|
namespace bwidgets::widget
|
|
{
|
|
template<typename T>
|
|
concept Numeric = std::is_arithmetic_v<T>;
|
|
|
|
template<typename T>
|
|
requires Numeric<T>
|
|
class Numeric_Input : public abstract::Input<T>
|
|
{
|
|
private:
|
|
Button _increment_button;
|
|
SDL_Rect _increment_button_area;
|
|
Button _decrement_button;
|
|
SDL_Rect _decrement_button_area;
|
|
std::pair<T, T> _value_range {
|
|
std::numeric_limits<T>::lowest(),
|
|
std::numeric_limits<T>::max()
|
|
};
|
|
|
|
virtual void _handle_font_change() override
|
|
{
|
|
_decrement_button.font(abstract::FontHandler::_font);
|
|
_increment_button.font(abstract::FontHandler::_font);
|
|
abstract::Input<T>::_handle_font_change();
|
|
}
|
|
|
|
virtual void _update_widget_area() noexcept override
|
|
{
|
|
abstract::Input<T>::_update_widget_area();
|
|
|
|
const int widest_button =
|
|
_increment_button.size().w
|
|
? _increment_button.size().w
|
|
: _decrement_button.size().w;
|
|
const int button_area_width = 2 * widest_button;
|
|
const int spacing = utils::math::center_rect(button_area_width, widest_button);
|
|
const int field_width = abstract::Widget::_viewport.w - 2 * button_area_width;
|
|
|
|
abstract::Input<T>::_input_area.x =
|
|
abstract::Widget::_widget_area.x + button_area_width;
|
|
abstract::Input<T>::_input_area.w = field_width;
|
|
|
|
_decrement_button_area = {
|
|
spacing,
|
|
utils::math::center_rect(abstract::Widget::_viewport.h, _decrement_button.size().h),
|
|
_decrement_button.size().w,
|
|
_decrement_button.size().h
|
|
};
|
|
|
|
_increment_button_area = {
|
|
abstract::Widget::_viewport.w - spacing - _increment_button.size().w,
|
|
utils::math::center_rect(abstract::Widget::_viewport.h, _increment_button.size().h),
|
|
_increment_button.size().w,
|
|
_increment_button.size().h
|
|
};
|
|
}
|
|
|
|
public:
|
|
T button_step = 1;
|
|
|
|
Numeric_Input(abstract::Widget* parent=nullptr)
|
|
: abstract::Input<T>(parent),
|
|
_increment_button(this),
|
|
_decrement_button(this)
|
|
{
|
|
abstract::Input<T>::_input_caption.alignment.h =
|
|
widget::Caption::AlignmentH::RIGHT;
|
|
|
|
_increment_button.text("+");
|
|
|
|
_increment_button.click_handler = [this](const SDL_MouseButtonEvent&) {
|
|
T new_value = this->value + button_step;
|
|
if (_value_range.second - this->value < button_step)
|
|
new_value = _value_range.second;
|
|
|
|
this->value = new_value;
|
|
this->input_text(this->value_to_string(new_value));
|
|
};
|
|
|
|
_decrement_button.text("-");
|
|
|
|
_decrement_button.click_handler = [this](const SDL_MouseButtonEvent&) {
|
|
T new_value = this->value - button_step;
|
|
if (this->value - _value_range.first < button_step)
|
|
new_value = _value_range.first;
|
|
|
|
this->value = new_value;
|
|
this->input_text(this->value_to_string(new_value));
|
|
};
|
|
}
|
|
|
|
virtual void handle_event(const SDL_Event& ev) override
|
|
{
|
|
abstract::Widget::handle_event(ev);
|
|
_increment_button.handle_event(ev);
|
|
_decrement_button.handle_event(ev);
|
|
}
|
|
|
|
virtual bool is_valid_input(const std::string input) const noexcept override
|
|
{
|
|
bool valid = false;
|
|
|
|
if (input.at(0) >= '0' && input.at(0) <= '9')
|
|
valid = true;
|
|
if constexpr(std::is_floating_point_v<T>)
|
|
if (input.at(0) == '.'
|
|
&& abstract::Input<T>::input_text().find('.') == std::string::npos)
|
|
valid = true;
|
|
if constexpr(std::is_signed_v<T>)
|
|
{
|
|
std::string displayed_value = abstract::Input<T>::input_text();
|
|
if (input.at(0) == '-'
|
|
&& !std::regex_search(
|
|
displayed_value,
|
|
std::regex("[^\\s]")
|
|
))
|
|
valid = true;
|
|
}
|
|
return valid;
|
|
}
|
|
|
|
virtual T process_value(T x) const noexcept override
|
|
{
|
|
T value = x;
|
|
if (x < _value_range.first)
|
|
value = _value_range.first;
|
|
else if (x > _value_range.second)
|
|
value = _value_range.second;
|
|
|
|
return value;
|
|
}
|
|
|
|
virtual void render(SDL_Renderer* r) override
|
|
{
|
|
abstract::Input<T>::render(r);
|
|
_increment_button.render(r);
|
|
_decrement_button.render(r);
|
|
}
|
|
|
|
virtual std::pair<T, T> value_range() const noexcept
|
|
{
|
|
return _value_range;
|
|
}
|
|
|
|
virtual void value_range(T min, T max)
|
|
{
|
|
|
|
if (min > max)
|
|
throw std::invalid_argument("min cannot be greater than max");
|
|
_value_range = {min, max};
|
|
}
|
|
|
|
virtual void viewport(const SDL_Rect& vp) noexcept override
|
|
{
|
|
abstract::Input<T>::viewport(vp);
|
|
|
|
_increment_button.viewport(utils::math::rect_offset(
|
|
_increment_button_area, abstract::Widget::_viewport
|
|
));
|
|
_decrement_button.viewport(utils::math::rect_offset(
|
|
_decrement_button_area, abstract::Widget::_viewport
|
|
));
|
|
}
|
|
};
|
|
}
|
|
|
|
#endif
|