bwidgets/inc/basic_widgets/w/base/input.hpp

244 lines
8.1 KiB
C++
Raw Normal View History

#ifndef BWIDGETS_INPUT_HPP_
#define BWIDGETS_INPUT_HPP_
2021-06-11 13:30:33 +02:00
#include <exception>
#include <iomanip>
2021-06-16 13:50:34 +02:00
#include <sstream>
2021-07-10 19:55:53 +02:00
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_keyboard.h>
#include <SDL2/SDL_render.h>
2021-06-16 13:50:34 +02:00
#include <basic_widgets/core/math.hpp>
#include <basic_widgets/core/type/concepts.hpp>
#include <basic_widgets/w/caption.hpp>
#include <basic_widgets/w/feat/keyboard_handler.hpp>
#include <basic_widgets/w/feat/mouse_handler.hpp>
2021-06-16 13:50:34 +02:00
namespace bwidgets::widget
2021-06-11 13:30:33 +02:00
{
2021-07-10 19:55:53 +02:00
template<typename T> class Input : public Widget,
public FontHandler,
public KeyboardHandler,
public MouseHandler
{
protected:
const int _border_width {3};
Caption _input_caption;
2021-07-10 19:55:53 +02:00
Input(Widget* parent=nullptr)
: Widget(parent)
{
2021-07-16 17:18:13 +02:00
FocusHandler::_focus_area = &_widget_area;
MouseHandler::_click_area = &_widget_area;
_input_caption.text(value_to_string(value));
2021-07-10 19:55:53 +02:00
}
2021-06-20 00:42:40 +02:00
virtual void _handle_focus_change(bool focus) override
2021-07-10 19:55:53 +02:00
{
if (focus)
2021-07-11 17:06:18 +02:00
{
2021-07-10 19:55:53 +02:00
SDL_StartTextInput();
2021-07-11 17:06:18 +02:00
}
2021-07-10 19:55:53 +02:00
else
{
value = value_from_string(input_text());
input_text(value_to_string(value));
2021-07-10 19:55:53 +02:00
SDL_StopTextInput();
}
}
2021-06-20 00:42:40 +02:00
virtual void _handle_font_change(core::Font* f) override
2021-07-10 19:55:53 +02:00
{
_input_caption.font(f);
2021-07-16 17:18:13 +02:00
_handle_geometry_change(_viewport);
}
2021-07-19 23:50:50 +02:00
virtual void _handle_font_color_change(const SDL_Color& fg,
const SDL_Color& bg) override
{
_input_caption.color_bg(bg)->color_fg(fg);
}
2021-07-16 17:18:13 +02:00
virtual void _handle_geometry_change(const SDL_Rect& vp) noexcept override
{
const auto input_h = _input_caption.size().h + 2 * _border_width;
_widget_area = {
0,
core::center_rect(vp.h, input_h),
2021-07-16 17:18:13 +02:00
vp.w,
input_h
};
_input_caption.viewport(
core::rect_offset({
core::rect_margin(_widget_area, {_border_width, _border_width})
2021-07-16 17:18:13 +02:00
}, vp
)
);
2021-07-10 19:55:53 +02:00
}
2021-06-20 00:42:40 +02:00
virtual void _handle_key(const SDL_KeyboardEvent& key) override
2021-06-20 00:42:40 +02:00
{
2021-07-10 19:55:53 +02:00
if (key.type == SDL_KEYDOWN)
2021-06-20 00:42:40 +02:00
{
2021-07-10 19:55:53 +02:00
switch (key.keysym.sym)
2021-06-20 00:42:40 +02:00
{
2021-07-10 19:55:53 +02:00
case SDLK_BACKSPACE:
{
std::string txt = input_text();
if (txt.length() > 0)
{
txt.pop_back();
input_text(txt);
}
break;
}
case SDLK_RETURN:
case SDLK_RETURN2: // what is return2 btw?
case SDLK_KP_ENTER:
value = value_from_string(input_text());
focus(false);
2021-07-10 19:55:53 +02:00
break;
2021-06-20 00:42:40 +02:00
}
}
}
virtual void _handle_renderer_change(core::Renderer* r) override
2021-07-10 19:55:53 +02:00
{
2021-07-16 17:18:13 +02:00
_input_caption.renderer(r);
}
virtual void _handle_rendering() override
{
// TODO: smoothstep
for (int i = _border_width - 1; i >= 0; i--)
2021-07-10 19:55:53 +02:00
{
2021-07-16 17:18:13 +02:00
_renderer->draw_color({
(uint8_t)(color_border.r / (i + 1)),
(uint8_t)(color_border.g / (i + 1)),
(uint8_t)(color_border.b / (i+ 1)),
color_border.a
})
->draw_rect({
2021-07-16 17:18:13 +02:00
_widget_area.x + i,
_widget_area.y + i,
_widget_area.w - 2 * i,
_widget_area.h - 2 * i
});
2021-07-10 19:55:53 +02:00
}
2021-07-16 17:18:13 +02:00
if (MouseHandler::_is_hovered || FocusHandler::_has_focus)
_input_caption.color_bg(color_bg_focused);
2021-07-16 17:18:13 +02:00
else
_input_caption.color_bg(color_bg);
2021-07-16 17:18:13 +02:00
2021-07-19 23:50:50 +02:00
2021-07-16 17:18:13 +02:00
_input_caption.render();
2021-07-10 19:55:53 +02:00
}
2021-06-11 13:30:33 +02:00
2021-07-16 17:18:13 +02:00
virtual void _handle_text_input(const SDL_TextInputEvent& input) override
2021-07-10 19:55:53 +02:00
{
2021-07-16 17:18:13 +02:00
if (is_valid_input(input.text))
{
input_text(input_text() + input.text);
}
2021-07-10 19:55:53 +02:00
}
2021-06-16 18:09:14 +02:00
2021-07-10 19:55:53 +02:00
public:
SDL_Color color_border {160, 160, 160, SDL_ALPHA_OPAQUE};
SDL_Color color_bg {200, 200, 200, SDL_ALPHA_OPAQUE};
2021-07-11 17:06:18 +02:00
SDL_Color color_bg_focused {255, 255, 255, SDL_ALPHA_OPAQUE};
int float_precision {2};
2021-07-23 20:08:52 +02:00
int input_min_width {1};
char input_width_unit {'W'};
T value {};
2021-06-16 18:09:14 +02:00
2021-07-19 23:50:50 +02:00
virtual Input<T>* color_fg(const SDL_Color& c) noexcept
2021-07-10 19:55:53 +02:00
{
_input_caption.color_fg(c);
2021-07-19 23:50:50 +02:00
return this;
2021-07-10 19:55:53 +02:00
}
2021-06-16 18:09:14 +02:00
virtual const std::string& input_text() const noexcept
2021-07-10 19:55:53 +02:00
{
return _input_caption.text();
}
2021-06-11 13:30:33 +02:00
2021-07-19 23:50:50 +02:00
virtual Input<T>* input_text(std::string txt) noexcept
2021-07-10 19:55:53 +02:00
{
_input_caption.text(txt);
2021-07-19 23:50:50 +02:00
return this;
2021-07-10 19:55:53 +02:00
}
virtual bool is_valid_input(const std::string) const noexcept
2021-07-10 19:55:53 +02:00
{
return true;
}
2021-06-11 13:30:33 +02:00
virtual T process_value(T x) const noexcept
2021-07-10 19:55:53 +02:00
{
return x;
}
2021-07-23 20:08:52 +02:00
virtual inline core::Size size() const noexcept override
{
return {
_font->text_size(
std::string(input_min_width, input_width_unit)
).w + 2 * _border_width,
_font->line_skip + 4 * _border_width // _why_ 4 and not 2?…
2021-07-23 20:08:52 +02:00
};
}
2021-07-10 19:55:53 +02:00
T value_from_string(std::string s)
{
T v;
2021-06-20 00:42:40 +02:00
2021-07-10 19:55:53 +02:00
if constexpr(std::is_arithmetic_v<T>)
{
s = s.length() > 0 ? s : "0";
if constexpr(std::is_floating_point_v<T>)
v = (T)std::stold(s);
else if constexpr(std::is_integral_v<T>)
v = (T)std::stoll(s);
2021-07-10 19:55:53 +02:00
}
else if constexpr(std::is_same_v<T, std::string> || std::convertible_to<std::string, T>)
v = s;
else
static_assert(sizeof(T) && false, "string cannot be converted to v type T.");
return process_value(v);
}
std::string value_to_string(T value)
{
std::string s;
if constexpr(std::is_same_v<std::string, T> || std::convertible_to<T, std::string>)
s = std::move(value);
else if constexpr(core::CanToString<T>)
2021-07-10 19:55:53 +02:00
{
std::stringstream ss;
ss << std::fixed
<< std::setprecision(float_precision)
<< value;
s = std::move(ss).str();
}
else if constexpr(core::Printable<T>)
2021-07-10 19:55:53 +02:00
{
std::stringstream ss;
ss << value;
s = std::move(ss).str();
}
else
static_assert(sizeof(T) && false, "value cannot be converted to string.");
2021-07-10 19:55:53 +02:00
return s;
}
};
}
2021-06-11 13:30:33 +02:00
#endif