fix Button, working numeric input, build static lib and examples.

This commit is contained in:
Andrea Blankenstijn 2021-06-22 20:32:38 +02:00
parent 9d939c33e8
commit 3d21921c4a
9 changed files with 268 additions and 61 deletions

View file

@ -5,6 +5,23 @@ Button::Button(SDL_Renderer* r)
{
}
int Button::border_width() const
{
return _border_width;
}
void Button::border_width(int w)
{
_border_width = w;
_update_widget_area();
}
int Button::height() const
{
return _caption.height() * _caption_height_multiplicator
+ 2 * _border_width;
}
void Button::render()
{
Widget::render();
@ -67,36 +84,41 @@ void Button::render()
SDL_RenderSetViewport(_renderer, &old_vp);
}
const std::string& Button::text() const
{
return _caption.text();
}
void Button::text(std::string txt)
{
_caption.text(txt);
_update_widget_area();
_update_caption_area();
}
void Button::font(TTF_Font* f)
{
_caption.font(f);
_update_widget_area();
_update_caption_area();
}
void Button::viewport(SDL_Rect vp)
{
Widget::viewport(vp);
_widget_area.y = center(
_viewport.h,
_caption.rendered_text_size().h * _caption_height_multiplicator
) - _border_width;
_update_caption_area();
}
int Button::width() const
{
return _caption.width() * _caption_height_multiplicator
+ 2 * _border_width;
}
void Button::_update_caption_area()
{
SDL_Rect txt_size = _caption.rendered_text_size();
_widget_area.w = txt_size.w + 2 * _border_width;
_widget_area.h = _caption.rendered_text_size().h * _caption_height_multiplicator
+ 2 * _border_width;
_caption_area = {
center(_viewport.w, txt_size.w),
center(_viewport.h, txt_size.h),
@ -110,3 +132,19 @@ void Button::_update_caption_area()
_caption_area.h
});
}
void Button::_update_widget_area()
{
int h = _caption.rendered_text_size().h * _caption_height_multiplicator
+ 2 * _border_width;
_widget_area = {
0,
center(
_viewport.h,
_caption.rendered_text_size().h * _caption_height_multiplicator
) - _border_width,
_viewport.w,
h
};
}

View file

@ -10,22 +10,28 @@
class Button : public Widget
{
protected:
static const int _border_width = 3;
int _border_width = 3;
Caption _caption;
SDL_Rect _caption_area;
static constexpr const float _caption_height_multiplicator = 1.2;
static constexpr const SDL_Color _color_background = {150, 150, 150, SDL_ALPHA_OPAQUE};
static constexpr const SDL_Color _color_background_hover = {175, 175, 175, SDL_ALPHA_OPAQUE};
static constexpr const SDL_Color _color_foreground = {0, 0, 0, SDL_ALPHA_OPAQUE};
float _caption_height_multiplicator = 1.2;
SDL_Color _color_background = {150, 150, 150, SDL_ALPHA_OPAQUE};
SDL_Color _color_background_hover = {175, 175, 175, SDL_ALPHA_OPAQUE};
SDL_Color _color_foreground = {0, 0, 0, SDL_ALPHA_OPAQUE};
void _update_caption_area();
void _update_widget_area();
public:
Button(SDL_Renderer*);
void render();
void text(std::string);
int border_width() const;
void border_width(int);
void font(TTF_Font*);
int height() const;
void render();
const std::string& text() const;
void text(std::string);
void viewport(SDL_Rect);
int width() const;
};
#endif

View file

@ -0,0 +1,85 @@
#include <iostream>
#include "button.hpp"
#include "horizontal_layout.hpp"
#include "vertical_layout.hpp"
int main()
{
int width = 640;
int height = 480;
TTF_Init();
TTF_Font* font = TTF_OpenFont("fonts/FiraMono-Bold.ttf", 20);
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* win = SDL_CreateWindow(
"Widget test",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
width,
height,
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE
);
SDL_Renderer* rend = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
SDL_SetRenderDrawBlendMode(rend, SDL_BLENDMODE_BLEND);
SDL_Rect layout_vp = {0, 0, width, height};
auto layout = new Horizontal_Layout(rend);
for (int i = 0; i < 3; i++)
{
auto column = new Vertical_Layout(rend);
for (int j = 0; j < 3; j++)
{
auto widget = new Button(rend);
widget->text("hello");
widget->click_handler = [i, j](const SDL_MouseButtonEvent&) {
std::cerr << "click@" << j << ":" << i << std::endl;
};
widget->font(font);
column->add_widget(widget);
std::cerr << " " << i << "@" << widget;
}
layout->add_widget(column);
}
layout->viewport(layout_vp);
bool quit = false;
while (!quit)
{
SDL_Event ev;
while (SDL_PollEvent(&ev) != 0)
{
switch (ev.type)
{
case SDL_QUIT:
quit = true;
break;
case SDL_WINDOWEVENT:
if (ev.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
{
SDL_GetRendererOutputSize(rend, &width, &height);
layout_vp.w = width;
layout_vp.h = height;
std::cerr << "== WINDOW RESIZED == " << width << "x" << height << std::endl;
layout->viewport(layout_vp);
}
break;
}
layout->handle_event(ev);
}
SDL_SetRenderDrawColor(rend, 75, 75, 75, SDL_ALPHA_OPAQUE);
SDL_RenderClear(rend);
layout->render();
SDL_RenderPresent(rend);
}
delete layout;
SDL_DestroyRenderer(rend);
SDL_DestroyWindow(win);
SDL_Quit();
TTF_CloseFont(font);
TTF_Quit();
return 0;
}

View file

@ -1,6 +1,5 @@
#include <iostream>
#include "button.hpp"
#include "horizontal_layout.hpp"
#include "numeric_input.hpp"
#include "vertical_layout.hpp"
@ -33,7 +32,9 @@ int main()
for (int j = 0; j < 3; j++)
{
auto widget = new Numeric_Input<float>(rend);
widget->button_step = 0.5;
widget->font(font);
widget->value_range(-2.5, 2.5);
column->add_widget(widget);
std::cerr << " " << i << "@" << widget;
}
@ -61,9 +62,6 @@ int main()
layout->viewport(layout_vp);
}
break;
case SDL_MOUSEBUTTONUP:
//_handle_click({ev.button.x, ev.button.y});
break;
}
layout->handle_event(ev);
}

View file

@ -19,8 +19,8 @@ void Horizontal_Layout::_update_layout()
(int)(widget_size - _interspace),
_viewport.h
};
_widgets.at(i)->viewport(widget_vp);
std::cerr << "h-layout:widget:" << i << " " << widget_vp.x << ","
std::cerr << "h-layout@" << this << ":widget:" << i << " " << widget_vp.x << ","
<< widget_vp.y << "," << widget_vp.w << "," << widget_vp.h << std::endl;
_widgets.at(i)->viewport(widget_vp);
}
}

View file

@ -1,6 +1,8 @@
#ifndef INPUT_HPP
#define INPUT_HPP
#include <exception>
#include <iomanip>
#include <sstream>
#include <string>
#include <type_traits>
@ -48,7 +50,7 @@ protected:
{
case SDLK_BACKSPACE:
{
std::string txt = _input_text.text();
std::string txt = input_text();
if (txt.length() > 0)
{
txt.pop_back();
@ -111,6 +113,7 @@ protected:
public:
SDL_Color border_color {160, 160, 160, SDL_ALPHA_OPAQUE};
SDL_Color background_color {200, 200, 200, SDL_ALPHA_OPAQUE};
int float_precision {2};
SDL_Color focused_background_color {255, 255, 255, SDL_ALPHA_OPAQUE};
virtual const std::string& input_text() const
@ -135,6 +138,11 @@ public:
_update_drawing_areas();
}
virtual T process_value(T x) const
{
return x;
}
virtual void render()
{
Widget::render();
@ -191,27 +199,34 @@ public:
_render_value();
}
static T value_from_string(std::string s)
T value_from_string(std::string s)
{
T value;
T v;
if constexpr(std::is_arithmetic_v<T>)
{
s = s.length() > 0 ? s : "0";
if constexpr(std::is_floating_point_v<T>)
value = (T)std::stold(s);
else if constexpr(std::is_integral_v<T>)
value = (T)std::stoll(s);
try
{
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);
}
catch(std::exception&)
{
v = value();
}
}
else if constexpr(std::is_same_v<T, std::string> || std::convertible_to<std::string, T>)
value = s;
v = s;
else
static_assert(sizeof(T) && false, "string cannot be converted to value type T.");
static_assert(sizeof(T) && false, "string cannot be converted to v type T.");
return value;
return process_value(v);
}
static std::string value_to_string(T value)
std::string value_to_string(T value)
{
std::string s;
@ -219,17 +234,17 @@ public:
s = value;
else if constexpr(CanToString<T>)
{
s = std::to_string(value);
for (char& last = s.back();
(last == '0' || last == '.') && s.length() > 1;
last = s.back())
s.pop_back();
std::stringstream ss;
ss << std::fixed
<< std::setprecision(float_precision)
<< value;
s = std::move(ss).str();
}
else if constexpr(Printable<T>)
{
std::stringstream ss;
ss << value;
return std::move(ss).str();
s = std::move(ss).str();
}
else
static_assert(sizeof(T) && false, "_value cannot be converted to string.");

View file

@ -2,16 +2,25 @@ project('sdl2_basic_widgets', 'cpp',
version : '0.1',
default_options : ['warning_level=3', 'cpp_std=c++20'])
executable('sdl2_basic_widgets',
'horizontal_layout.cpp',
'layout.cpp',
lib = static_library('sdl2_basic_widgets',
'horizontal_layout.cpp',
'layout.cpp',
'button.cpp',
'caption.cpp',
'vertical_layout.cpp',
'widget.cpp',
'widget_test.cpp',
dependencies : [
dependency('sdl2'),
dependency('SDL2_ttf')
],
install : true)
executable('button_demo',
'examples/button_example.cpp',
link_with : lib,
install : false)
executable('input_demo',
'examples/input_example.cpp',
link_with : lib,
install : false)

View file

@ -1,6 +1,8 @@
#ifndef NUMERIC_INPUT_HPP
#define NUMERIC_INPUT_HPP
#include <limits>
#include <regex>
#include <type_traits>
#include "button.hpp"
@ -18,6 +20,10 @@ private:
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()
};
void handle_event(const SDL_Event& ev)
{
@ -28,37 +34,44 @@ private:
void _update_drawing_areas()
{
int widest_button = _increment_button.width() > _decrement_button.width() ?
_increment_button.width() : _decrement_button.width();
int heighest_button = _increment_button.height() > _decrement_button.height() ?
_increment_button.height() : _decrement_button.height();
int field_width = Widget::_viewport.w - 2 * 1.25 * widest_button;
int field_height = heighest_button;
const int widest_button =
_increment_button.width() > _decrement_button.width()
? _increment_button.width()
: _decrement_button.width();
const int heighest_button =
_increment_button.height() > _decrement_button.height()
? _increment_button.height()
: _decrement_button.height();
const int button_area_width = 2 * widest_button;
const int spacing = Widget::center(button_area_width, widest_button);
const int field_width = Widget::_viewport.w - 2 * button_area_width;
const int field_height = heighest_button;
Input<T>::_input_field_area = {
(int)(1.25 * widest_button),
(int)button_area_width,
Widget::center(Widget::_viewport.h, field_height),
field_width,
field_height
};
_increment_button_area = {
Widget::center(1.25 * widest_button, _increment_button.width()),
spacing,
Widget::center(Widget::_viewport.h, _increment_button.height()),
_increment_button.width(),
_increment_button.height()
};
_decrement_button_area = {
Widget::_viewport.w - (Widget::center(1.25 * widest_button, _decrement_button.width()) + _decrement_button.width()),
Widget::_viewport.w - spacing - _decrement_button.width(),
Widget::center(Widget::_viewport.h, _decrement_button.height()),
_decrement_button.width(),
_decrement_button.height()
};
Widget::_widget_area = {
Widget::_viewport.x + Widget::center(1.25 * widest_button, widest_button),
Widget::_viewport.y + Widget::center(Widget::_viewport.h, heighest_button),
(int)(1.25 * widest_button / 2) + Widget::_viewport.w - widest_button - Widget::_viewport.x,
button_area_width,
Widget::center(Widget::_viewport.h, heighest_button),
Widget::_viewport.w
- 2 * button_area_width,
heighest_button
};
@ -75,13 +88,21 @@ public:
{
_increment_button.text("+");
_increment_button.click_handler = [this](const SDL_MouseButtonEvent&) {
this->value(this->value() + button_step);
this->input_text(this->value_to_string(this->value()));
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&) {
this->value(this->value() - button_step);
this->input_text(this->value_to_string(this->value()));
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));
};
}
@ -99,11 +120,33 @@ public:
if (input.at(0) >= '0' && input.at(0) <= '9')
valid = true;
if constexpr(std::is_floating_point_v<T>)
if (input.at(0) == '.')
if (input.at(0) == '.'
&& Input<T>::input_text().find('.') == std::string::npos)
valid = true;
if constexpr(std::is_signed_v<T>)
{
std::string displayed_value = Input<T>::input_text();
if (input.at(0) == '-'
&& !std::regex_search(
displayed_value,
std::regex("[^\\s]")
))
valid = true;
}
return valid;
}
T process_value(T x) const
{
T value = x;
if (x < _value_range.first)
value = _value_range.first;
else if (x > _value_range.second)
value = _value_range.second;
return value;
}
void render()
{
Input<T>::render();
@ -111,6 +154,19 @@ public:
_decrement_button.render();
}
std::pair<T, T> value_range() const
{
return _value_range;
}
void value_range(T min, T max)
{
if (min > max)
throw std::invalid_argument("min cannot be greater than max");
_value_range = {min, max};
}
void viewport(SDL_Rect vp)
{
Input<T>::viewport(vp);

View file

@ -19,8 +19,8 @@ void Vertical_Layout::_update_layout()
_viewport.w,
(int)(widget_size - _interspace),
};
_widgets.at(i)->viewport(widget_vp);
std::cerr << "v-layout:widget:" << i << " " << widget_vp.x << ","
std::cerr << "v-layout@" << this << ":widget:" << i << " " << widget_vp.x << ","
<< widget_vp.y << "," << widget_vp.w << "," << widget_vp.h << std::endl;
_widgets.at(i)->viewport(widget_vp);
}
}