fix Button, working numeric input, build static lib and examples.
This commit is contained in:
parent
9d939c33e8
commit
3d21921c4a
54
button.cpp
54
button.cpp
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
20
button.hpp
20
button.hpp
|
@ -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
|
||||
|
|
85
examples/button_example.cpp
Normal file
85
examples/button_example.cpp
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
49
input.hpp
49
input.hpp
|
@ -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.");
|
||||
|
|
17
meson.build
17
meson.build
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue