wip big rewrite

This commit is contained in:
Andrea Blankenstijn 2021-07-10 19:55:53 +02:00
parent 5a427ec4c2
commit deb11ed9fd
32 changed files with 1124 additions and 898 deletions

View File

@ -1,17 +1,22 @@
#include <iostream>
#include <SDL2/SDL_ttf.h>
#include <basic_widgets/button.hpp>
#include <basic_widgets/horizontal_layout.hpp>
#include <basic_widgets/utils/font.hpp>
#include <basic_widgets/vertical_layout.hpp>
using namespace bwidgets::widget;
using namespace bwidgets::utils;
int main()
{
int width = 640;
int height = 480;
TTF_Init();
TTF_Font* font = TTF_OpenFont(find_font("Monospace").c_str(), 20);
TTF_Font* font = TTF_OpenFont(font::find("Monospace").c_str(), 20);
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* win = SDL_CreateWindow(
@ -26,13 +31,13 @@ int main()
SDL_SetRenderDrawBlendMode(rend, SDL_BLENDMODE_BLEND);
SDL_Rect layout_vp = {0, 0, width, height};
auto layout = new Horizontal_Layout(rend);
auto layout = new Horizontal_Layout();
for (int i = 0; i < 3; i++)
{
auto column = new Vertical_Layout(rend);
auto column = new Vertical_Layout();
for (int j = 0; j < 3; j++)
{
auto widget = new Button(rend);
auto widget = new Button();
widget->text("hello");
widget->click_handler = [i, j](const SDL_MouseButtonEvent&) {
std::cerr << "click@" << j << ":" << i << std::endl;
@ -72,7 +77,7 @@ int main()
SDL_SetRenderDrawColor(rend, 75, 75, 75, SDL_ALPHA_OPAQUE);
SDL_RenderClear(rend);
layout->render();
layout->render(rend);
SDL_RenderPresent(rend);
}

View File

@ -5,13 +5,16 @@
#include <basic_widgets/utils/font.hpp>
#include <basic_widgets/vertical_layout.hpp>
using namespace bwidgets::utils;
using namespace bwidgets::widget;
int main()
{
int width = 640;
int height = 480;
TTF_Init();
TTF_Font* font = TTF_OpenFont(find_font("Monospace").c_str(), 20);
TTF_Font* font = TTF_OpenFont(font::find("Monospace").c_str(), 20);
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* win = SDL_CreateWindow(
@ -26,13 +29,13 @@ int main()
SDL_SetRenderDrawBlendMode(rend, SDL_BLENDMODE_BLEND);
SDL_Rect layout_vp = {0, 0, width, height};
auto layout = new Horizontal_Layout(rend);
auto layout = new Horizontal_Layout();
for (int i = 0; i < 3; i++)
{
auto column = new Vertical_Layout(rend);
auto column = new Vertical_Layout();
for (int j = 0; j < 3; j++)
{
auto widget = new Numeric_Input<float>(rend);
auto widget = new Numeric_Input<float>();
widget->button_step = 0.5;
widget->font(font);
widget->value_range(-2.5, 2.5);
@ -70,7 +73,7 @@ int main()
SDL_SetRenderDrawColor(rend, 75, 75, 75, SDL_ALPHA_OPAQUE);
SDL_RenderClear(rend);
layout->render();
layout->render(rend);
SDL_RenderPresent(rend);
}

View File

@ -0,0 +1,27 @@
#ifndef FOCUS_HANDLER_HPP
#define FOCUS_HANDLER_HPP
struct SDL_Rect;
namespace bwidgets::abstract
{
class FocusHandler
{
protected:
const SDL_Rect* _focus_area = nullptr;
bool _has_focus = false;
virtual void _handle_focus_change();
public:
virtual ~FocusHandler();
inline virtual void focus(bool focus)
{
_has_focus = focus;
_handle_focus_change();
}
};
}
#endif

View File

@ -0,0 +1,21 @@
#ifndef FONT_HANDLER_HPP
#define FONT_HANDLER_HPP
#include <SDL2/SDL_ttf.h>
namespace bwidgets::abstract
{
class FontHandler
{
protected:
TTF_Font* _font {nullptr};
virtual void _handle_font_change() = 0;
public:
virtual ~FontHandler();
virtual void font(TTF_Font*);
};
}
#endif

View File

@ -7,255 +7,245 @@
#include <string>
#include <type_traits>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_keyboard.h>
#include <SDL2/SDL_render.h>
#include <basic_widgets/abstract/keyboard_handler.hpp>
#include <basic_widgets/abstract/mouse_handler.hpp>
#include <basic_widgets/caption.hpp>
#include <basic_widgets/utils/math.hpp>
template<typename T>
concept CanToString = requires(T value) { std::to_string(value); };
template<typename T>
concept Printable = requires(T value) { std::declval<std::ostream&>() << value; };
template<typename T> class Input : public Widget
namespace bwidgets::abstract
{
protected:
SDL_Rect _input_area {};
SDL_Rect _input_field_area {};
Caption _input_text;
T _value {};
SDL_Rect _input_text_area {};
template<typename T>
concept CanToString = requires(T value) { std::to_string(value); };
Input(SDL_Renderer* r)
: Widget(r), _input_text(r)
{
input_text(value_to_string(_value));
}
template<typename T>
concept Printable = requires(T value) { std::declval<std::ostream&>() << value; };
virtual void _on_focus()
template<typename T> class Input : public Widget,
public FontHandler,
public KeyboardHandler,
public MouseHandler
{
SDL_StartTextInput();
}
protected:
const int _border_width {3};
SDL_Rect _input_area {};
widget::Caption _input_caption;
SDL_Rect _input_caption_area {};
T _value {};
virtual void _on_focus_loss()
{
input_text(value_to_string(_value));
SDL_StopTextInput();
}
virtual void _on_key(const SDL_KeyboardEvent& key)
{
if (key.type == SDL_KEYDOWN)
{
switch (key.keysym.sym)
Input(Widget* parent=nullptr)
: Widget(parent)
{
case SDLK_BACKSPACE:
MouseHandler::_focus_area = &_input_area;
MouseHandler::_button_area = &_input_area;
_input_caption.text(value_to_string(_value));
}
virtual void _handle_focus_change()
{
if (MouseHandler::_has_focus)
SDL_StartTextInput();
else
{
std::string txt = input_text();
if (txt.length() > 0)
{
txt.pop_back();
input_text(txt);
value(value_from_string(txt));
}
break;
}
case SDLK_RETURN:
case SDLK_RETURN2: // what is return2 btw?
case SDLK_KP_ENTER:
input_text(value_to_string(_value));
break;
SDL_StopTextInput();
}
}
}
}
virtual void _on_text_input(const SDL_TextInputEvent& input)
{
Widget::_on_text_input(input);
if (is_valid_input(input.text))
{
std::string new_txt_value = input_text() + input.text;
input_text(new_txt_value);
value(value_from_string(new_txt_value));
}
}
virtual void _render_value()
{
_update_drawing_areas();
}
virtual void _update_drawing_areas()
{
_update_widget_area();
_input_area = {
_input_field_area.x + 3,
_input_field_area.y + 3,
_input_field_area.w - 6,
_input_field_area.h - 6
};
auto text_render_size = _input_text.rendered_text_size();
int text_area_width = _input_area.h / (float)text_render_size.h * text_render_size.w;
SDL_Rect value_text_vp = {
_viewport.x + _input_area.x + Widget::center(_input_area.w, text_area_width),
_viewport.y + _input_area.y,
text_area_width,
_input_area.h
};
_input_text.viewport(value_text_vp);
//SDL_SetTextInputRect(&_input_area);
}
virtual void _update_widget_area()
{
_widget_area = _input_field_area;
}
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
{
return _input_text.text();
}
virtual void input_text(std::string txt)
{
_input_text.text(txt);
_update_drawing_areas();
}
virtual bool is_valid_input(const std::string) const
{
return true;
}
virtual void font(TTF_Font* f)
{
_input_text.font(f);
_update_drawing_areas();
}
virtual T process_value(T x) const
{
return x;
}
virtual void render()
{
Widget::render();
SDL_Rect old_vp;
SDL_RenderGetViewport(_renderer, &old_vp);
SDL_RenderSetViewport(_renderer, &_viewport);
for (int i = 3; i > 0; i--)
{
SDL_SetRenderDrawColor(
_renderer,
border_color.r / i,
border_color.g / i,
border_color.b / i,
border_color.a
);
SDL_Rect border_area = {
_input_field_area.x + 3 - i,
_input_field_area.y + 3 - i,
_input_field_area.w - 2 * (3 - i),
_input_field_area.h - 2 * (3 - i)
};
SDL_RenderDrawRect(_renderer, &border_area);
}
SDL_Color* bg_color = &background_color;
if (_focused)
bg_color = &focused_background_color;
SDL_SetRenderDrawColor(
_renderer,
bg_color->r,
bg_color->g,
bg_color->b,
bg_color->a
);
SDL_RenderFillRect(_renderer, &_input_area);
_input_text.render();
SDL_RenderSetViewport(_renderer, &old_vp);
}
T value() const
{
return _value;
}
void value(T new_value)
{
_value = new_value;
_render_value();
}
T value_from_string(std::string s)
{
T v;
if constexpr(std::is_arithmetic_v<T>)
{
s = s.length() > 0 ? s : "0";
try
virtual void _handle_font_change()
{
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);
_input_caption.font(_font);
}
catch(std::exception&)
virtual void _handle_key(const SDL_KeyboardEvent& key)
{
v = value();
if (key.type == SDL_KEYDOWN)
{
switch (key.keysym.sym)
{
case SDLK_BACKSPACE:
{
std::string txt = input_text();
if (txt.length() > 0)
{
txt.pop_back();
input_text(txt);
value(value_from_string(txt));
}
break;
}
case SDLK_RETURN:
case SDLK_RETURN2: // what is return2 btw?
case SDLK_KP_ENTER:
input_text(value_to_string(_value));
break;
}
}
}
}
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);
}
virtual void _handle_text_input(const SDL_TextInputEvent& input)
{
if (is_valid_input(input.text))
{
std::string new_txt_value = input_text() + input.text;
input_text(new_txt_value);
value(value_from_string(new_txt_value));
}
}
std::string value_to_string(T value)
{
std::string s;
virtual void _update_widget_area()
{
_widget_area = {
_viewport.x,
utils::math::center_rect(_viewport.h, _input_caption.height()),
_viewport.w,
_input_caption.height()
};
_input_area = {
_widget_area.x + _border_width,
_widget_area.y + _border_width,
_widget_area.w - 2 * _border_width,
_widget_area.h - 2 * _border_width
};
if constexpr(std::is_same_v<std::string, T> || std::convertible_to<T, std::string>)
s = std::move(value);
else if constexpr(CanToString<T>)
{
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;
s = std::move(ss).str();
}
else
static_assert(sizeof(T) && false, "_value cannot be converted to string.");
_input_caption.viewport(
utils::math::rect_offset(_input_area, _viewport)
);
}
return s;
}
public:
SDL_Color color_border {160, 160, 160, SDL_ALPHA_OPAQUE};
SDL_Color color_bg {200, 200, 200, SDL_ALPHA_OPAQUE};
int float_precision {2};
SDL_Color focused_bg_color {255, 255, 255, SDL_ALPHA_OPAQUE};
virtual void viewport(SDL_Rect vp)
{
Widget::viewport(vp);
_update_drawing_areas();
}
};
virtual void color_fg(const SDL_Color& c)
{
_input_caption.color_fg(c);
}
virtual const std::string& input_text() const
{
return _input_caption.text();
}
virtual void input_text(std::string txt)
{
_input_caption.text(txt);
}
virtual bool is_valid_input(const std::string) const
{
return true;
}
virtual T process_value(T x) const
{
return x;
}
virtual void render(SDL_Renderer* r)
{
Widget::render(r);
SDL_RenderSetViewport(r, &_viewport);
for (int i = 3; i > 0; i--)
{
SDL_SetRenderDrawColor(
r,
color_border.r / i,
color_border.g / i,
color_border.b / i,
color_border.a
);
SDL_Rect border_area = {
_widget_area.x + 3 - i,
_widget_area.y + 3 - i,
_widget_area.w - 2 * (3 - i),
_widget_area.h - 2 * (3 - i)
};
SDL_RenderDrawRect(r, &border_area);
}
SDL_Color* c = &color_bg;
if (MouseHandler::_has_focus)
c = &focused_bg_color;
SDL_SetRenderDrawColor(r, c->r, c->g, c->b, c->a);
SDL_RenderFillRect(r, &_input_area);
_input_caption.render(r);
}
T value() const
{
return _value;
}
void value(T new_value)
{
_value = new_value;
_input_caption.text(value_to_string(_value));
_update_widget_area();
}
T value_from_string(std::string s)
{
T v;
if constexpr(std::is_arithmetic_v<T>)
{
s = s.length() > 0 ? s : "0";
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>)
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(CanToString<T>)
{
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;
s = std::move(ss).str();
}
else
static_assert(sizeof(T) && false, "_value cannot be converted to string.");
return s;
}
};
}
#endif

Binary file not shown.

View File

@ -0,0 +1,33 @@
#ifndef KEYBOARD_HANDLER
#define KEYBOARD_HANDLER
#include <basic_widgets/abstract/focus_handler.hpp>
struct SDL_KeyboardEvent;
struct SDL_TextInputEvent;
namespace bwidgets::abstract
{
class KeyboardHandler : public FocusHandler
{
protected:
virtual void _handle_key(const SDL_KeyboardEvent&);
virtual void _handle_text_input(const SDL_TextInputEvent&);
public:
inline virtual void handle_keyboard(const SDL_KeyboardEvent& ev)
{
if (_has_focus)
_handle_key(ev);
}
inline virtual void handle_keyboard(const SDL_TextInputEvent& ev)
{
if (_has_focus)
_handle_text_input(ev);
}
};
}
#endif

View File

@ -5,21 +5,23 @@
#include <basic_widgets/abstract/widget.hpp>
class Layout : public Widget
namespace bwidgets::abstract
{
protected:
float _interspace = 16;
std::vector<Widget*> _widgets;
class Layout : public abstract::Widget
{
protected:
int _interspace = 16;
std::vector<Widget*> _widgets;
virtual void _update_layout() = 0;
virtual void _update_layout() = 0;
public:
Layout(SDL_Renderer*);
virtual ~Layout();
virtual void add_widget(Widget*);
virtual void handle_event(const SDL_Event&);
virtual void render();
virtual void viewport(SDL_Rect);
};
public:
virtual ~Layout();
virtual void add_widget(Widget*);
virtual void handle_event(const SDL_Event&);
virtual void render(SDL_Renderer*);
virtual void viewport(SDL_Rect);
};
}
#endif

View File

@ -0,0 +1,37 @@
#ifndef MOUSE_HANDLER_HPP
#define MOUSE_HANDLER_HPP
#include <functional>
#include <basic_widgets/abstract/focus_handler.hpp>
struct SDL_MouseButtonEvent;
struct SDL_MouseMotionEvent;
struct SDL_Rect;
namespace bwidgets::abstract
{
class MouseHandler : public FocusHandler
{
protected:
const SDL_Rect* _button_area = nullptr;
bool _is_hovered = false;
bool _is_pushed = false;
virtual void _handle_focus_change();
virtual void _handle_mouse_button(const SDL_MouseButtonEvent&,
const SDL_Rect&);
virtual void _handle_mouse_motion(const SDL_MouseMotionEvent&,
const SDL_Rect&);
public:
std::function<void (const SDL_MouseButtonEvent&)> click_handler;
virtual void handle_mouse(const SDL_MouseButtonEvent&,
const SDL_Rect&);
virtual void handle_mouse(const SDL_MouseMotionEvent&,
const SDL_Rect&);
};
}
#endif

View File

@ -3,41 +3,38 @@
#include <functional>
#include <SDL2/SDL.h>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_rect.h>
class Widget
struct SDL_Renderer;
namespace bwidgets::abstract
{
protected:
bool _focused = false;
bool _hovered = false;
bool _pushed = false;
SDL_Renderer* _renderer;
SDL_Rect _viewport {0, 0, 0, 0};
SDL_Rect _widget_area {0, 0, 0, 0};
virtual void _on_focus();
virtual void _on_focus_loss();
virtual void _on_key(const SDL_KeyboardEvent&);
virtual void _on_push(const SDL_MouseButtonEvent&);
virtual void _on_release(const SDL_MouseButtonEvent&);
virtual void _on_text_input(const SDL_TextInputEvent&);
virtual void _update_hover_state(const SDL_MouseMotionEvent&);
virtual void _update_widget_area();
public:
std::function<void (const SDL_MouseButtonEvent&)> click_handler = NULL;
Widget(SDL_Renderer*);
virtual ~Widget();
static inline int center(int available_size, int used_size)
class Widget
{
return (available_size - used_size) / 2;
}
virtual void handle_event(const SDL_Event&);
virtual int height() const;
virtual void render();
virtual void viewport(SDL_Rect);
virtual int width() const;
};
protected:
SDL_Rect _viewport {0, 0, 0, 0};
SDL_Rect _widget_area {0, 0, 0, 0};
virtual void _update_widget_area();
public:
struct Size {
int w;
int h;
};
Widget* parent;
Widget(Widget* parent=nullptr);
virtual ~Widget();
virtual void handle_event(const SDL_Event&);
virtual int height() const;
virtual void render(SDL_Renderer*);
virtual void viewport(SDL_Rect);
virtual int width() const;
};
}
#endif

View File

@ -3,35 +3,38 @@
#include <string>
#include <SDL2/SDL_ttf.h>
#include <basic_widgets/abstract/mouse_handler.hpp>
#include <basic_widgets/caption.hpp>
class Button : public Widget
namespace bwidgets::widget
{
protected:
int _border_width = 3;
Caption _caption;
SDL_Rect _caption_area;
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};
class Button : public abstract::Widget,
public abstract::FontHandler,
public abstract::MouseHandler
{
protected:
widget::Caption _caption;
SDL_Rect _caption_area;
float _caption_height_multiplicator = 1.2;
SDL_Color _color_foreground = {0, 0, 0, SDL_ALPHA_OPAQUE};
void _update_caption_area();
void _update_widget_area();
void _handle_font_change();
public:
Button(SDL_Renderer*);
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;
};
void _update_caption_area();
void _update_widget_area();
public:
int border_width {3};
SDL_Color color_bg {150, 150, 150, SDL_ALPHA_OPAQUE};
SDL_Color color_bg_hover {175, 175, 175, SDL_ALPHA_OPAQUE};
int height() const;
void render(SDL_Renderer*);
const std::string& text() const;
void text(std::string);
void viewport(SDL_Rect);
int width() const;
};
}
#endif

View File

@ -3,32 +3,47 @@
#include <string>
#include <SDL2/SDL_ttf.h>
#include <basic_widgets/abstract/font_handler.hpp>
#include <basic_widgets/abstract/widget.hpp>
class Caption : public Widget
struct SDL_Surface;
struct SDL_Texture;
namespace bwidgets::widget
{
SDL_Color _color = {0, 0, 0, SDL_ALPHA_OPAQUE};
TTF_Font* _font = NULL;
std::string _text;
SDL_Texture* _rendered_text = NULL;
class Caption : public abstract::Widget, public abstract::FontHandler
{
public:
enum struct Alignment {
CENTER,
LEFT,
RIGHT
};
void _render_text();
void _update_widget_area();
private:
Alignment _alignment {Alignment::RIGHT};
SDL_Color _color_fg {0, 0, 0, SDL_ALPHA_OPAQUE};
std::string _text;
SDL_Surface* _rendered_text {nullptr};
SDL_Texture* _text_texture {nullptr};
SDL_Renderer* _renderer {nullptr};
public:
Caption(SDL_Renderer*);
~Caption();
void color(SDL_Color);
void font(TTF_Font*);
int height() const;
void render();
const std::string& text() const;
void text(std::string);
SDL_Rect rendered_text_size() const;
void viewport(SDL_Rect);
int width() const;
};
void _handle_font_change();
void _render_text();
void _update_widget_area();
public:
SDL_Color color_bg {0, 0, 0, SDL_ALPHA_TRANSPARENT};
~Caption();
void align(Alignment);
void color_fg(const SDL_Color&);
void render(SDL_Renderer*);
abstract::Widget::Size text_size() const;
const std::string& text() const;
void text(const std::string&);
};
}
#endif

View File

@ -3,13 +3,13 @@
#include <basic_widgets/abstract/layout.hpp>
class Horizontal_Layout : public Layout
namespace bwidgets::widget
{
protected:
void _update_layout();
public:
Horizontal_Layout(SDL_Renderer*);
};
class Horizontal_Layout : public abstract::Layout
{
private:
void _update_layout();
};
}
#endif

View File

@ -5,185 +5,188 @@
#include <regex>
#include <type_traits>
#include <basic_widgets/button.hpp>
#include <basic_widgets/abstract/input.hpp>
#include <basic_widgets/button.hpp>
#include <basic_widgets/utils/math.hpp>
template<typename T>
concept Numeric = std::is_arithmetic_v<T>;
template<typename T>
requires Numeric<T>
class Numeric_Input : public Input<T>
namespace bwidgets::widget
{
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()
};
template<typename T>
concept Numeric = std::is_arithmetic_v<T>;
void handle_event(const SDL_Event& ev)
{
Widget::handle_event(ev);
_increment_button.handle_event(ev);
_decrement_button.handle_event(ev);
}
void _update_drawing_areas()
{
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)button_area_width,
Widget::center(Widget::_viewport.h, field_height),
field_width,
field_height
};
_increment_button_area = {
spacing,
Widget::center(Widget::_viewport.h, _increment_button.height()),
_increment_button.width(),
_increment_button.height()
};
_decrement_button_area = {
Widget::_viewport.w - spacing - _decrement_button.width(),
Widget::center(Widget::_viewport.h, _decrement_button.height()),
_decrement_button.width(),
_decrement_button.height()
};
Widget::_widget_area = {
button_area_width,
Widget::center(Widget::_viewport.h, heighest_button),
Widget::_viewport.w
- 2 * button_area_width,
heighest_button
};
Input<T>::_update_drawing_areas();
}
public:
T button_step = 1;
Numeric_Input(SDL_Renderer* r)
: Input<T>(r),
_increment_button(r),
_decrement_button(r)
{
_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));
};
}
void font(TTF_Font* f)
{
_decrement_button.font(f);
_increment_button.font(f);
Input<T>::font(f);
}
bool is_valid_input(const std::string input) const
{
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) == '.'
&& Input<T>::input_text().find('.') == std::string::npos)
valid = true;
if constexpr(std::is_signed_v<T>)
template<typename T>
requires Numeric<T>
class Numeric_Input : public abstract::Input<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;
}
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()
};
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;
void handle_event(const SDL_Event& ev)
{
abstract::Widget::handle_event(ev);
_increment_button.handle_event(ev);
_decrement_button.handle_event(ev);
}
return value;
}
void _update_drawing_areas()
{
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 = utils::math::center_rect(button_area_width, widest_button);
const int field_width = abstract::Widget::_viewport.w - 2 * button_area_width;
const int field_height = heighest_button;
abstract::Input<T>::_input_field_area = {
(int)button_area_width,
utils::math::center_rect(abstract::Widget::_viewport.h, field_height),
field_width,
field_height
};
void render()
{
Input<T>::render();
_increment_button.render();
_decrement_button.render();
}
_increment_button_area = {
spacing,
utils::math::center_rect(abstract::Widget::_viewport.h, _increment_button.height()),
_increment_button.width(),
_increment_button.height()
};
std::pair<T, T> value_range() const
{
return _value_range;
}
_decrement_button_area = {
abstract::Widget::_viewport.w - spacing - _decrement_button.width(),
utils::math::center_rect(abstract::Widget::_viewport.h, _decrement_button.height()),
_decrement_button.width(),
_decrement_button.height()
};
void value_range(T min, T max)
{
abstract::Widget::_widget_area = {
button_area_width,
utils::math::center_rect(abstract::Widget::_viewport.h, heighest_button),
abstract::Widget::_viewport.w
- 2 * button_area_width,
heighest_button
};
if (min > max)
throw std::invalid_argument("min cannot be greater than max");
_value_range = {min, max};
}
abstract::Input<T>::_update_drawing_areas();
}
void viewport(SDL_Rect vp)
{
Input<T>::viewport(vp);
public:
T button_step = 1;
_increment_button.viewport({
Widget::_viewport.x + _increment_button_area.x,
Widget::_viewport.y + _increment_button_area.y,
_increment_button_area.w,
_increment_button_area.h
});
_decrement_button.viewport({
Widget::_viewport.x + _decrement_button_area.x,
Widget::_viewport.y + _decrement_button_area.y,
_decrement_button_area.w,
_decrement_button_area.h
});
}
};
Numeric_Input(abstract::Widget* parent=nullptr)
: abstract::Input<T>(parent),
_increment_button(), // FIXME: parenthood
_decrement_button()
{
_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));
};
}
void handle_font_chage()
{
_decrement_button.font(abstract::FontHandler::_font);
_increment_button.font(abstract::FontHandler::_font);
}
bool is_valid_input(const std::string input) const
{
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;
}
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(SDL_Renderer* r)
{
abstract::Input<T>::render(r);
_increment_button.render(r);
_decrement_button.render(r);
}
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)
{
abstract::Input<T>::viewport(vp);
_increment_button.viewport({
abstract::Widget::_viewport.x + _increment_button_area.x,
abstract::Widget::_viewport.y + _increment_button_area.y,
_increment_button_area.w,
_increment_button_area.h
});
_decrement_button.viewport({
abstract::Widget::_viewport.x + _decrement_button_area.x,
abstract::Widget::_viewport.y + _decrement_button_area.y,
_decrement_button_area.w,
_decrement_button_area.h
});
}
};
}
#endif

View File

@ -4,8 +4,14 @@
#include <exception>
#include <string>
struct NotFound : std::exception {};
namespace bwidgets::exception
{
struct NotFound : std::exception {};
}
std::string find_font(const std::string&);
namespace bwidgets::utils::font
{
std::string find(const std::string&);
}
#endif

View File

@ -0,0 +1,47 @@
#ifndef MATH_HPP
#define MATH_HPP
#include <exception>
#include <SDL2/SDL_rect.h>
namespace bwidgets::utils::math
{
struct RectOverflow : std::exception {};
static inline int center_rect(int container_size, int rect_size)
{
return (container_size - rect_size) / 2;
}
static inline float distance_sqrd(const SDL_Point& a, const SDL_Point& b)
{
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
static inline float distance(const SDL_Point& a, const SDL_Point& b)
{
return std::sqrt(distance_sqrd(a, b));
}
static inline bool rect_in_rect(const SDL_Rect outer, const SDL_Rect inner)
{
SDL_Point top_left {inner.x, inner.y};
SDL_Point bottom_right {inner.x + inner.w, inner.y + inner.h};
return
SDL_PointInRect(&top_left, &outer) &&
SDL_PointInRect(&bottom_right, &outer);
}
static inline SDL_Rect rect_offset(const SDL_Rect& r, const SDL_Rect& offset)
{
return {
r.x + offset.x,
r.y + offset.y,
r.w, r.h
};
}
}
#endif

View File

@ -9,20 +9,26 @@
struct SDL_Texture;
SDL_Texture* filled_circle(const SDL_Color&,
int resolution,
SDL_Renderer*,
int aa_pixels = 5);
void set_pixels_color(SDL_Texture*, std::function<Uint32 (const SDL_Point&,
const SDL_PixelFormat*)>);
namespace bwidgets::utils::render
{
SDL_Color aa(const SDL_Color&&, const int, const float);
static inline int render_copy(SDL_Renderer* r, SDL_Texture* t, SDL_Rect dst)
{
return SDL_RenderCopy(r, t, NULL, &dst);
}
static inline int render_copy(SDL_Renderer* r, SDL_Texture* t, SDL_Rect src, SDL_Rect dst)
{
return SDL_RenderCopy(r, t, &src, &dst);
SDL_Texture* filled_circle(const SDL_Color&,
int resolution,
SDL_Renderer*,
int aa_pixels = 5);
void set_pixels_color(SDL_Texture*, std::function<Uint32 (const SDL_Point&,
const SDL_PixelFormat*)>);
static inline int render_copy(SDL_Renderer* r, SDL_Texture* t, SDL_Rect dst)
{
return SDL_RenderCopy(r, t, NULL, &dst);
}
static inline int render_copy(SDL_Renderer* r, SDL_Texture* t, SDL_Rect src, SDL_Rect dst)
{
return SDL_RenderCopy(r, t, &src, &dst);
}
}
#endif

View File

@ -3,13 +3,13 @@
#include <basic_widgets/abstract/layout.hpp>
class Vertical_Layout : public Layout
namespace bwidgets::widget
{
protected:
void _update_layout();
public:
Vertical_Layout(SDL_Renderer*);
};
class Vertical_Layout : public abstract::Layout
{
protected:
void _update_layout();
};
}
#endif

View File

@ -14,11 +14,14 @@ pub_api = include_directories('inc')
libbasic_widgets = static_library('basic_widgets',
'src/button.cpp',
'src/caption.cpp',
'src/focus_handler.cpp',
'src/font_handler.cpp',
'src/horizontal_layout.cpp',
'src/keyboard_handler.cpp',
'src/layout.cpp',
'src/mouse_handler.cpp',
'src/vertical_layout.cpp',
'src/widget.cpp',
'src/with_font.cpp',
'src/utils/render.cpp',
'src/utils/font.cpp',
dependencies : [sdl, fontconfig],

View File

@ -1,51 +1,38 @@
#include <SDL2/SDL_render.h>
#include <basic_widgets/button.hpp>
#include <basic_widgets/utils/math.hpp>
Button::Button(SDL_Renderer* r)
: Widget(r), _caption(r)
{
}
using namespace bwidgets;
using namespace bwidgets::utils;
int Button::border_width() const
{
return _border_width;
}
void Button::border_width(int w)
{
_border_width = w;
_update_widget_area();
}
int Button::height() const
int widget::Button::height() const
{
return _caption.height() * _caption_height_multiplicator
+ 2 * _border_width;
+ 2 * border_width;
}
void Button::render()
void widget::Button::render(SDL_Renderer* r)
{
Widget::render();
Widget::render(r);
SDL_Rect old_vp;
SDL_RenderGetViewport(_renderer, &old_vp);
SDL_RenderSetViewport(_renderer, &_viewport);
SDL_RenderSetViewport(r, &_viewport);
auto color_shade = [](int base, int step, int total_steps) -> int {
int steps_to_max = 255 - base;
return base + step * steps_to_max / total_steps;
};
SDL_Color bg_color = _hovered ? _color_background_hover : _color_background;
if (_pushed)
SDL_Color bg_color = _is_hovered ? color_bg_hover : color_bg;
if (_is_pushed)
{
for (int i = 0; i < _border_width; i++)
for (int i = 0; i < border_width; i++)
{
SDL_SetRenderDrawColor(
_renderer,
color_shade(bg_color.r, i, _border_width),
color_shade(bg_color.g, i, _border_width),
color_shade(bg_color.b, i, _border_width),
r,
color_shade(bg_color.r, i, border_width),
color_shade(bg_color.g, i, border_width),
color_shade(bg_color.b, i, border_width),
bg_color.a
);
SDL_Rect rect = {
@ -54,74 +41,72 @@ void Button::render()
_widget_area.w - i * 2,
_widget_area.h - i * 2
};
SDL_RenderFillRect(_renderer, &rect);
SDL_RenderFillRect(r, &rect);
}
}
else
{
for (int i = _border_width; i >= 0; i--)
for (int i = border_width; i >= 0; i--)
{
SDL_SetRenderDrawColor(
_renderer,
color_shade(bg_color.r, i, _border_width),
color_shade(bg_color.g, i, _border_width),
color_shade(bg_color.b, i, _border_width),
r,
color_shade(bg_color.r, i, border_width),
color_shade(bg_color.g, i, border_width),
color_shade(bg_color.b, i, border_width),
bg_color.a
);
int margin = _border_width - i;
int margin = border_width - i;
SDL_Rect rect = {
_widget_area.x + margin,
_widget_area.y + margin,
_widget_area.w - margin * 2,
_widget_area.h - margin * 2
};
SDL_RenderFillRect(_renderer, &rect);
SDL_RenderFillRect(r, &rect);
}
}
_caption.render();
SDL_RenderSetViewport(_renderer, &old_vp);
_caption.render(r);
}
const std::string& Button::text() const
const std::string& widget::Button::text() const
{
return _caption.text();
}
void Button::text(std::string txt)
void widget::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)
void widget::Button::viewport(SDL_Rect vp)
{
Widget::viewport(vp);
_update_caption_area();
}
int Button::width() const
int widget::Button::width() const
{
return _caption.width() * _caption_height_multiplicator
+ 2 * _border_width;
+ 2 * border_width;
}
void Button::_update_caption_area()
void widget::Button::_handle_font_change()
{
SDL_Rect txt_size = _caption.rendered_text_size();
_caption.font(_font);
_update_widget_area();
_update_caption_area();
}
void widget::Button::_update_caption_area()
{
auto txt_size = _caption.text_size();
_caption_area = {
center(_viewport.w, txt_size.w),
center(_viewport.h, txt_size.h),
math::center_rect(_viewport.w, txt_size.w),
math::center_rect(_viewport.h, txt_size.h),
txt_size.w,
txt_size.h
};
@ -133,17 +118,17 @@ void Button::_update_caption_area()
});
}
void Button::_update_widget_area()
void widget::Button::_update_widget_area()
{
int h = _caption.rendered_text_size().h * _caption_height_multiplicator
+ 2 * _border_width;
int h = _caption.text_size().h * _caption_height_multiplicator
+ 2 * border_width;
_widget_area = {
0,
center(
math::center_rect(
_viewport.h,
_caption.rendered_text_size().h * _caption_height_multiplicator
) - _border_width,
_caption.text_size().h * _caption_height_multiplicator
) - border_width,
_viewport.w,
h
};

View File

@ -1,128 +1,122 @@
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_ttf.h>
#include <basic_widgets/caption.hpp>
#include <basic_widgets/utils/math.hpp>
Caption::Caption(SDL_Renderer* r)
: Widget(r)
using namespace bwidgets;
using namespace bwidgets::utils;
widget::Caption::~Caption()
{
SDL_FreeSurface(_rendered_text);
SDL_DestroyTexture(_text_texture);
}
Caption::~Caption()
void widget::Caption::color_fg(const SDL_Color& c)
{
SDL_DestroyTexture(_rendered_text);
}
void Caption::color(SDL_Color c)
{
_color = c;
_color_fg = c;
_render_text();
}
void Caption::font(TTF_Font* f)
void widget::Caption::_handle_font_change()
{
_font = f;
_render_text();
}
int Caption::height() const
void widget::Caption::render(SDL_Renderer* r)
{
return _widget_area.h;
Widget::render(r);
_renderer = r;
if (_font == nullptr)
return;
SDL_RenderSetViewport(r, &_viewport);
if (_text_texture == nullptr || _renderer != r)
{
_renderer = r;
_text_texture = SDL_CreateTextureFromSurface(r, _rendered_text);
}
SDL_RenderCopy(
r,
_text_texture,
NULL,
&_widget_area
);
}
void Caption::render()
void widget::Caption::_render_text()
{
if (_font == nullptr)
return;
Widget::render();
SDL_Rect old_vp;
SDL_RenderGetViewport(_renderer, &old_vp);
SDL_RenderSetViewport(_renderer, &_viewport);
SDL_SetRenderDrawColor(_renderer, 255, 255, 0, 50);
SDL_RenderFillRect(_renderer, &_widget_area);
SDL_RenderCopy(
_renderer,
_rendered_text,
NULL,
&_widget_area
SDL_FreeSurface(_rendered_text);
_rendered_text = TTF_RenderUTF8_Blended(
_font,
_text.c_str(),
_color_fg
);
SDL_RenderSetViewport(_renderer, &old_vp);
_update_widget_area();
}
const std::string& Caption::text() const
const std::string& widget::Caption::text() const
{
return _text;
}
void Caption::text(std::string t)
void widget::Caption::text(const std::string& t)
{
_text = t;
_render_text();
}
SDL_Rect Caption::rendered_text_size() const
abstract::Widget::Size widget::Caption::text_size() const
{
if (_font == nullptr)
return {0, 0, 0, 0};
return {0, 0};
int w, h;
TTF_SizeUTF8(_font, _text.c_str(), &w, &h);
abstract::Widget::Size size;
TTF_SizeUTF8(_font, _text.c_str(), &size.w, &size.h);
return {0, 0, w, h};
return size;
}
void Caption::viewport(SDL_Rect vp)
{
Widget::viewport(vp);
_update_widget_area();
}
int Caption::width() const
{
return _widget_area.w;
}
void Caption::_render_text()
{
if (_font == nullptr)
return;
SDL_Surface* surface = TTF_RenderUTF8_Blended(
_font,
_text.c_str(),
_color
);
SDL_DestroyTexture(_rendered_text);
_rendered_text = SDL_CreateTextureFromSurface(_renderer, surface);
SDL_FreeSurface(surface);
_update_widget_area();
}
void Caption::_update_widget_area()
void widget::Caption::_update_widget_area()
{
Widget::_update_widget_area();
if (_font == nullptr)
return;
auto txt_size = rendered_text_size();
auto txt_size = text_size();
SDL_Rect resized_txt = {};
if (_widget_area.w > _widget_area.h)
{
resized_txt.h = _widget_area.h;
resized_txt.w = resized_txt.h / (float)txt_size.h * txt_size.w;
_widget_area.x = Widget::center(_widget_area.w, resized_txt.w);
resized_txt.w = resized_txt.h / (txt_size.h * txt_size.w);
switch (_alignment)
{
case Alignment::CENTER:
_widget_area.x = math::center_rect(_widget_area.w, resized_txt.w);
break;
case Alignment::LEFT:
break;
case Alignment::RIGHT:
_widget_area.x = _widget_area.w - resized_txt.w;
}
}
else
{
resized_txt.w = _widget_area.w;
if (txt_size.w > 0)
resized_txt.h = resized_txt.w / (float)txt_size.w * txt_size.h;
resized_txt.h = resized_txt.w / (txt_size.w * txt_size.h);
else
resized_txt.h = txt_size.w;
_widget_area.y = Widget::center(_widget_area.h, resized_txt.h);
_widget_area.y = math::center_rect(_widget_area.h, resized_txt.h);
}
}

11
src/focus_handler.cpp Normal file
View File

@ -0,0 +1,11 @@
#include <basic_widgets/abstract/focus_handler.hpp>
using namespace bwidgets;
abstract::FocusHandler::~FocusHandler()
{
}
void abstract::FocusHandler::_handle_focus_change()
{
}

13
src/font_handler.cpp Normal file
View File

@ -0,0 +1,13 @@
#include <basic_widgets/abstract/font_handler.hpp>
using namespace bwidgets;
abstract::FontHandler::~FontHandler()
{
}
void abstract::FontHandler::font(TTF_Font* f)
{
_font = f;
_handle_font_change();
}

View File

@ -1,22 +1,19 @@
#include <basic_widgets/horizontal_layout.hpp>
Horizontal_Layout::Horizontal_Layout(SDL_Renderer* r)
: Layout::Layout(r)
{
}
using namespace bwidgets;
void Horizontal_Layout::_update_layout()
void widget::Horizontal_Layout::_update_layout()
{
float widget_size = (float)_viewport.w / _widgets.size();
int widget_size = _viewport.w / _widgets.size();
for (std::vector<Widget*>::size_type i = 0; i < _widgets.size(); i++)
{
SDL_Rect widget_vp = {
_viewport.x + (int)(_interspace / 2 + i * (widget_size + _interspace / 2)),
_widgets.at(i)->viewport({
_viewport.x + (int)(_interspace / 2.0 +
i * (widget_size + _interspace / 2.0)),
_viewport.y,
(int)(widget_size - _interspace),
widget_size - _interspace,
_viewport.h
};
_widgets.at(i)->viewport(widget_vp);
});
}
}

13
src/keyboard_handler.cpp Normal file
View File

@ -0,0 +1,13 @@
#include <SDL2/SDL_events.h>
#include <basic_widgets/abstract/keyboard_handler.hpp>
using namespace bwidgets;
void abstract::KeyboardHandler::_handle_key(const SDL_KeyboardEvent&)
{
}
void abstract::KeyboardHandler::_handle_text_input(const SDL_TextInputEvent&)
{
}

View File

@ -1,44 +1,38 @@
#include <SDL2/SDL_render.h>
#include <basic_widgets/abstract/layout.hpp>
Layout::Layout(SDL_Renderer* r)
: Widget(r)
{
}
using namespace bwidgets;
Layout::~Layout()
abstract::Layout::~Layout()
{
for (Widget* widget_ptr : _widgets)
delete widget_ptr;
}
void Layout::add_widget(Widget *widget_ptr)
void abstract::Layout::add_widget(Widget *widget_ptr)
{
_widgets.push_back(widget_ptr);
}
void Layout::handle_event(const SDL_Event& ev)
void abstract::Layout::handle_event(const SDL_Event& ev)
{
for (Widget* widget_ptr : _widgets)
widget_ptr->handle_event(ev);
}
void Layout::render()
void abstract::Layout::render(SDL_Renderer* r)
{
SDL_Rect old_vp;
SDL_RenderSetViewport(r, &_viewport);
SDL_RenderGetViewport(_renderer, &old_vp);
SDL_RenderSetViewport(_renderer, &_viewport);
SDL_SetRenderDrawColor(_renderer, 255, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderDrawRect(_renderer, NULL);
SDL_SetRenderDrawColor(r, 255, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderDrawRect(r, NULL);
for (Widget* widget_ptr : _widgets)
widget_ptr->render();
SDL_RenderSetViewport(_renderer, &old_vp);
widget_ptr->render(r);
}
void Layout::viewport(SDL_Rect vp)
void abstract::Layout::viewport(SDL_Rect vp)
{
Widget::viewport(vp);
_update_layout();

71
src/mouse_handler.cpp Normal file
View File

@ -0,0 +1,71 @@
#include <SDL2/SDL_rect.h>
#include <SDL2/SDL_events.h>
#include <basic_widgets/abstract/mouse_handler.hpp>
#include <basic_widgets/utils/math.hpp>
using namespace bwidgets;
using namespace bwidgets::utils;
void abstract::MouseHandler::handle_mouse(const SDL_MouseButtonEvent& ev,
const SDL_Rect& orig)
{
if (_button_area == nullptr)
return;
SDL_Point p {ev.x, ev.y};
SDL_Rect vp = math::rect_offset(*_button_area, orig);
if (ev.type == SDL_MOUSEBUTTONDOWN)
{
if (SDL_PointInRect(&p, &vp))
{
_is_pushed = true;
_handle_mouse_button(ev, vp);
}
else
focus(false);
}
else
{
if (_is_pushed)
{
if (SDL_PointInRect(&p, &vp))
{
focus(true);
}
_is_pushed = false;
_handle_mouse_button(ev, vp);
}
}
}
void abstract::MouseHandler::handle_mouse(const SDL_MouseMotionEvent& ev,
const SDL_Rect& orig)
{
if (_focus_area == nullptr)
return;
SDL_Point p {ev.x, ev.y};
SDL_Rect vp {math::rect_offset(*_button_area, orig)};
if (SDL_PointInRect(&p, &vp))
_is_hovered = true;
else
_is_hovered = false;
if (_has_focus)
_handle_mouse_motion(ev, orig);
}
void abstract::MouseHandler::_handle_focus_change()
{
}
void abstract::MouseHandler::_handle_mouse_button(const SDL_MouseButtonEvent&,
const SDL_Rect&)
{
}
void abstract::MouseHandler::_handle_mouse_motion(const SDL_MouseMotionEvent&,
const SDL_Rect&)
{
}

View File

@ -3,35 +3,36 @@
#include <basic_widgets/utils/font.hpp>
using namespace std;
string find_font(const string& pat)
namespace bwidgets::utils::font
{
FcConfig* conf = FcInitLoadConfigAndFonts();
FcPattern* pattern = FcNameParse((FcChar8*)pat.c_str());
FcConfigSubstitute(conf, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
bool found = false;
string file_path;
FcResult res;
FcPattern* font = FcFontMatch(conf, pattern, &res);
if (font != nullptr)
std::string find(const std::string& pat)
{
FcChar8* file = nullptr;
if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch)
FcConfig* conf = FcInitLoadConfigAndFonts();
FcPattern* pattern = FcNameParse((FcChar8*)pat.c_str());
FcConfigSubstitute(conf, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
bool found = false;
std::string file_path;
FcResult res;
FcPattern* font = FcFontMatch(conf, pattern, &res);
if (font != nullptr)
{
file_path = (char*)file;
found = true;
FcChar8* file = nullptr;
if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch)
{
file_path = (char*)file;
found = true;
}
FcPatternDestroy(font);
}
FcPatternDestroy(font);
FcConfigDestroy(conf);
FcFini();
if (!found)
throw exception::NotFound();
return file_path;
}
FcConfigDestroy(conf);
FcFini();
if (!found)
throw NotFound();
return file_path;
}

View File

@ -1,80 +1,84 @@
#include <vector>
#include "render_priv.hpp"
#include <basic_widgets/utils/math.hpp>
#include <basic_widgets/utils/render.hpp>
SDL_Color aa(
const SDL_Color& base_color,
const int aa_pixels,
const float d
)
namespace bwidgets::utils::render
{
const auto d_clamp {std::abs(std::clamp<float>(d, -aa_pixels, 0))};
const auto d_norm {d_clamp / aa_pixels};
const auto factor {3 * d_norm * d_norm - 2 * d_norm * d_norm * d_norm};
return {
base_color.r, base_color.g, base_color.b,
static_cast<Uint8>(base_color.a * factor)
};
}
SDL_Texture* filled_circle(
const SDL_Color& c,
const int resolution,
SDL_Renderer* r,
const int aa_pixels
)
{
auto texture {SDL_CreateTexture(
r,
SDL_PIXELFORMAT_RGBA32,
SDL_TEXTUREACCESS_STATIC,
resolution, resolution)};
const auto radius {resolution / 2};
const SDL_Point center {radius, radius};
set_pixels_color(
texture,
[aa_pixels, c, center, radius]
(const SDL_Point& p, const SDL_PixelFormat* format) -> Uint32 {
const auto d_delta = distance(center, p) - radius;
const auto aa_color = aa(c, aa_pixels, d_delta);
return SDL_MapRGBA(format,
aa_color.r, aa_color.g, aa_color.b,
aa_color.a);
}
);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureScaleMode(texture, SDL_ScaleModeNearest);
return texture;
}
void set_pixels_color(
SDL_Texture* t,
std::function<Uint32 (const SDL_Point&, const SDL_PixelFormat*)> pixel_color
)
{
int w, h;
Uint32 format_enum;
SDL_QueryTexture(t, &format_enum, NULL, &w, &h);
auto format = SDL_AllocFormat(format_enum);
auto pitch = w * format->BytesPerPixel;
std::vector<Uint32> pixels;
pixels.reserve(h * pitch);
for (auto y = 0; y < h; y++)
SDL_Color aa(
const SDL_Color& base_color,
const int aa_pixels,
const float d
)
{
for (auto x = 0; x < w; x++)
{
pixels.push_back(pixel_color({x, y}, format));
}
const auto d_clamp {std::abs(std::clamp<float>(d, -aa_pixels, 0))};
const auto d_norm {d_clamp / aa_pixels};
const auto factor {3 * d_norm * d_norm - 2 * d_norm * d_norm * d_norm};
return {
base_color.r, base_color.g, base_color.b,
static_cast<Uint8>(base_color.a * factor)
};
}
SDL_UpdateTexture(t, NULL, (const void*)pixels.data(), pitch);
SDL_Texture* filled_circle(
const SDL_Color& c,
const int resolution,
SDL_Renderer* r,
const int aa_pixels
)
{
auto texture {SDL_CreateTexture(
r,
SDL_PIXELFORMAT_RGBA32,
SDL_TEXTUREACCESS_STATIC,
resolution, resolution)};
const auto radius {resolution / 2};
const SDL_Point center {radius, radius};
SDL_FreeFormat(format);
bwidgets::utils::render::set_pixels_color(
texture,
[aa_pixels, c, center, radius]
(const SDL_Point& p, const SDL_PixelFormat* format) -> Uint32 {
const auto d_delta = math::distance(center, p) - radius;
const auto aa_color = aa(c, aa_pixels, d_delta);
return SDL_MapRGBA(format,
aa_color.r, aa_color.g, aa_color.b,
aa_color.a);
}
);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureScaleMode(texture, SDL_ScaleModeNearest);
return texture;
}
void set_pixels_color(
SDL_Texture* t,
std::function<Uint32 (const SDL_Point&, const SDL_PixelFormat*)> pixel_color
)
{
int w, h;
Uint32 format_enum;
SDL_QueryTexture(t, &format_enum, NULL, &w, &h);
auto format = SDL_AllocFormat(format_enum);
auto pitch = w * format->BytesPerPixel;
std::vector<Uint32> pixels;
pixels.reserve(h * pitch);
for (auto y = 0; y < h; y++)
{
for (auto x = 0; x < w; x++)
{
pixels.push_back(pixel_color({x, y}, format));
}
}
SDL_UpdateTexture(t, NULL, (const void*)pixels.data(), pitch);
SDL_FreeFormat(format);
}
}

View File

@ -1,17 +0,0 @@
#ifndef DRAWING_PRIMITIVE_PRIVATE
#define DRAWING_PRIMITIVE_PRIVATE
#include <basic_widgets/utils/render.hpp>
SDL_Color aa(const SDL_Color&&, const int, const float);
static inline float distance_sqrd(const SDL_Point& a, const SDL_Point& b)
{
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
static inline float distance(const SDL_Point& a, const SDL_Point& b)
{
return std::sqrt(distance_sqrd(a, b));
}
#endif

View File

@ -1,21 +1,18 @@
#include <basic_widgets/vertical_layout.hpp>
Vertical_Layout::Vertical_Layout(SDL_Renderer* r)
: Layout(r)
{
}
using namespace bwidgets;
void Vertical_Layout::_update_layout()
void widget::Vertical_Layout::_update_layout()
{
float widget_size = (float)_viewport.h / _widgets.size();
int widget_size = _viewport.h / _widgets.size();
for (std::vector<Widget*>::size_type i = 0; i < _widgets.size(); i++)
for (int i = 0; i < _widgets.size(); i++)
{
SDL_Rect widget_vp = {
_viewport.x,
_viewport.y + (int)(_interspace / 2 + i * (widget_size + _interspace / 2)),
_viewport.y + _interspace / 2 + i * (widget_size + _interspace / 2),
_viewport.w,
(int)(widget_size - _interspace),
widget_size - _interspace,
};
_widgets.at(i)->viewport(widget_vp);
}

View File

@ -1,117 +1,82 @@
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_render.h>
#include <basic_widgets/abstract/keyboard_handler.hpp>
#include <basic_widgets/abstract/mouse_handler.hpp>
#include <basic_widgets/abstract/widget.hpp>
Widget::Widget(SDL_Renderer* r)
: _renderer(r)
using namespace bwidgets;
abstract::Widget::Widget(Widget* p)
: parent(p)
{
}
Widget::~Widget()
abstract::Widget::~Widget()
{
}
void Widget::handle_event(const SDL_Event &ev)
void abstract::Widget::handle_event(const SDL_Event &ev)
{
switch (ev.type)
if (auto handler = dynamic_cast<KeyboardHandler*>(this);
handler != nullptr)
{
case SDL_MOUSEMOTION:
_update_hover_state(ev.motion);
break;
case SDL_MOUSEBUTTONDOWN:
_on_push(ev.button);
break;
case SDL_MOUSEBUTTONUP:
_on_release(ev.button);
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
if (_focused)
_on_key(ev.key);
break;
case SDL_TEXTINPUT:
if (_focused)
_on_text_input(ev.text);
break;
switch (ev.type)
{
case SDL_KEYDOWN:
case SDL_KEYUP:
handler->handle_keyboard(ev.key);
break;
case SDL_TEXTINPUT:
handler->handle_keyboard(ev.text);
break;
}
}
if (auto handler = dynamic_cast<MouseHandler*>(this);
handler != nullptr)
{
switch (ev.type)
{
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
handler->handle_mouse(ev.button, _viewport);
break;
case SDL_MOUSEMOTION:
handler->handle_mouse(ev.motion, _viewport);
break;
}
}
}
int Widget::height() const
int abstract::Widget::height() const
{
return _widget_area.h;
}
void Widget::render()
void abstract::Widget::render(SDL_Renderer* r)
{
SDL_Rect old_vp;
SDL_RenderGetViewport(_renderer, &old_vp);
SDL_RenderSetViewport(_renderer, &_viewport);
SDL_SetRenderDrawColor(_renderer, 0, 255, 0, SDL_ALPHA_OPAQUE);
SDL_RenderDrawRect(_renderer, NULL);
SDL_RenderSetViewport(_renderer, &old_vp);
SDL_RenderSetViewport(r, NULL);
SDL_Rect dbg_outline = {
_viewport.x - 1, _viewport.y - 1,
_viewport.w + 2, _viewport.h + 2
};
SDL_SetRenderDrawColor(r, 0, 255, 0, SDL_ALPHA_OPAQUE / 2);
SDL_RenderDrawRect(r, &dbg_outline);
}
void Widget::viewport(SDL_Rect vp)
void abstract::Widget::viewport(SDL_Rect vp)
{
_viewport = vp;
_update_widget_area();
}
int Widget::width() const
int abstract::Widget::width() const
{
return _widget_area.w;
}
void Widget::_on_focus()
{
}
void Widget::_on_focus_loss()
{
}
void Widget::_on_key(const SDL_KeyboardEvent&)
{
}
void Widget::_on_push(const SDL_MouseButtonEvent& button)
{
SDL_Point position = {button.x - _viewport.x, button.y - _viewport.y};
if (SDL_PointInRect(&position, &_widget_area) == SDL_TRUE)
{
_pushed = true;
}
else if (_focused)
{
_focused = false;
_on_focus_loss();
}
}
void Widget::_on_release(const SDL_MouseButtonEvent& button)
{
if (_pushed)
{
_focused = true;
_on_focus();
if (click_handler != NULL)
click_handler(button);
}
_pushed = false;
}
void Widget::_on_text_input(const SDL_TextInputEvent&)
{
}
void Widget::_update_hover_state(const SDL_MouseMotionEvent& mouse)
{
SDL_Point position = {mouse.x - _viewport.x, mouse.y - _viewport.y};
_hovered = SDL_PointInRect(&position, &_widget_area) == SDL_TRUE;
}
void Widget::_update_widget_area()
void abstract::Widget::_update_widget_area()
{
_widget_area = {
0,