basic sdl wrapper

This commit is contained in:
Andrea Blankenstijn 2021-07-16 17:18:13 +02:00
parent 4e4539695f
commit 98f51426b8
30 changed files with 1110 additions and 443 deletions

View File

@ -27,8 +27,8 @@ int main()
height,
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE
);
SDL_Renderer* rend = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
SDL_SetRenderDrawBlendMode(rend, SDL_BLENDMODE_BLEND);
auto rend = bwidgets::sdl::Renderer(win, -1, SDL_RENDERER_ACCELERATED);
rend.blend_mode(SDL_BLENDMODE_BLEND);
SDL_Rect layout_vp = {0, 0, width, height};
auto layout = new Horizontal_Layout();
@ -49,6 +49,7 @@ int main()
layout->add_widget(column);
}
layout->viewport(layout_vp);
layout->renderer(&rend);
bool quit = false;
while (!quit)
{
@ -63,10 +64,10 @@ int main()
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;
auto size = rend.output_size();
layout_vp.w = size.w;
layout_vp.h = size.h;
std::cerr << "== WINDOW RESIZED == " << size.w << "x" << size.h << std::endl;
layout->viewport(layout_vp);
}
break;
@ -74,15 +75,14 @@ int main()
layout->handle_event(ev);
}
SDL_SetRenderDrawColor(rend, 75, 75, 75, SDL_ALPHA_OPAQUE);
SDL_RenderClear(rend);
rend.draw_color({75, 75, 75, SDL_ALPHA_OPAQUE});
rend.clear();
layout->render(rend);
SDL_RenderPresent(rend);
layout->render();
rend.present();
}
delete layout;
SDL_DestroyRenderer(rend);
SDL_DestroyWindow(win);
SDL_Quit();
TTF_CloseFont(font);

View File

@ -27,8 +27,8 @@ int main()
height,
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE
);
SDL_Renderer* rend = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
SDL_SetRenderDrawBlendMode(rend, SDL_BLENDMODE_BLEND);
bwidgets::sdl::Renderer rend(win, -1, SDL_RENDERER_ACCELERATED);
rend.blend_mode(SDL_BLENDMODE_BLEND);
SDL_Rect layout_vp = {0, 0, width, height};
auto layout = new Horizontal_Layout();
@ -46,6 +46,7 @@ int main()
layout->add_widget(column);
}
layout->viewport(layout_vp);
layout->renderer(&rend);
bool quit = false;
while (!quit)
{
@ -60,10 +61,10 @@ int main()
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;
auto size = rend.output_size();
layout_vp.w = size.w;
layout_vp.h = size.h;
std::cerr << "== WINDOW RESIZED == " << size.w << "x" << size.h << std::endl;
layout->viewport(layout_vp);
}
break;
@ -71,15 +72,14 @@ int main()
layout->handle_event(ev);
}
SDL_SetRenderDrawColor(rend, 75, 75, 75, SDL_ALPHA_OPAQUE);
SDL_RenderClear(rend);
rend.draw_color({75, 75, 75, SDL_ALPHA_OPAQUE});
rend.clear();
layout->render(rend);
SDL_RenderPresent(rend);
layout->render();
rend.present();
}
delete layout;
SDL_DestroyRenderer(rend);
SDL_DestroyWindow(win);
SDL_Quit();
TTF_CloseFont(font);

View File

@ -25,8 +25,8 @@ int main()
height,
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE
);
SDL_Renderer* rend = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
SDL_SetRenderDrawBlendMode(rend, SDL_BLENDMODE_BLEND);
auto rend = bwidgets::sdl::Renderer(win, -1, SDL_RENDERER_ACCELERATED);
rend.blend_mode(SDL_BLENDMODE_BLEND);
SDL_Rect layout_vp = {0, 0, width, height};
auto layout = new Horizontal_Layout();
@ -44,6 +44,7 @@ int main()
layout->add_widget(column);
}
layout->viewport(layout_vp);
layout->renderer(&rend);
bool quit = false;
while (!quit)
{
@ -58,10 +59,10 @@ int main()
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;
auto size = rend.output_size();
layout_vp.w = size.w;
layout_vp.h = size.h;
std::cerr << "== WINDOW RESIZED == " << size.w << "x" << size.h << std::endl;
layout->viewport(layout_vp);
}
break;
@ -69,15 +70,14 @@ int main()
layout->handle_event(ev);
}
SDL_SetRenderDrawColor(rend, 75, 75, 75, SDL_ALPHA_OPAQUE);
SDL_RenderClear(rend);
rend.draw_color({75, 75, 75, SDL_ALPHA_OPAQUE});
rend.clear();
layout->render(rend);
SDL_RenderPresent(rend);
layout->render();
rend.present();
}
delete layout;
SDL_DestroyRenderer(rend);
SDL_DestroyWindow(win);
SDL_Quit();
TTF_CloseFont(font);

View File

@ -32,14 +32,13 @@ namespace bwidgets::abstract
{
protected:
const int _border_width {3};
SDL_Rect _input_area {};
widget::Caption _input_caption;
Input(Widget* parent=nullptr)
: Widget(parent)
{
FocusHandler::_focus_area = &_input_area;
MouseHandler::_click_area = &_input_area;
FocusHandler::_focus_area = &_widget_area;
MouseHandler::_click_area = &_widget_area;
_input_caption.text(value_to_string(value));
}
@ -60,7 +59,25 @@ namespace bwidgets::abstract
virtual void _handle_font_change() override
{
_input_caption.font(_font);
_update_widget_area();
_handle_geometry_change(_viewport);
}
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,
utils::math::center_rect(vp.h, input_h),
vp.w,
input_h
};
_input_caption.viewport(
utils::math::rect_offset({
utils::math::rect_margin(_widget_area, {_border_width, _border_width})
}, vp
)
);
}
virtual void _handle_key(const SDL_KeyboardEvent& key) override
@ -89,6 +106,37 @@ namespace bwidgets::abstract
}
}
virtual void _handle_renderer_change(sdl::Renderer* r) override
{
_input_caption.renderer(r);
}
virtual void _handle_rendering() override
{
// TODO: smoothstep
for (int i = _border_width - 1; i >= 0; i--)
{
_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({
_widget_area.x + i,
_widget_area.y + i,
_widget_area.w - 2 * i,
_widget_area.h - 2 * i
});
}
if (MouseHandler::_is_hovered || FocusHandler::_has_focus)
_input_caption.color_bg = color_bg_focused;
else
_input_caption.color_bg = color_bg;
_input_caption.render();
}
virtual void _handle_text_input(const SDL_TextInputEvent& input) override
{
if (is_valid_input(input.text))
@ -97,23 +145,6 @@ namespace bwidgets::abstract
}
}
virtual void _update_widget_area() noexcept override
{
_widget_area = {
0,
utils::math::center_rect(_viewport.h,
_input_caption.size().h + 2 * _border_width),
_viewport.w,
_input_caption.size().h + 2 * _border_width
};
_input_area = {
_widget_area.x + _border_width,
_widget_area.y + _border_width,
_widget_area.w - 2 * _border_width,
_widget_area.h - 2 * _border_width
};
}
public:
SDL_Color color_border {160, 160, 160, SDL_ALPHA_OPAQUE};
SDL_Color color_bg {200, 200, 200, SDL_ALPHA_OPAQUE};
@ -146,50 +177,6 @@ namespace bwidgets::abstract
return x;
}
virtual void render(SDL_Renderer* r) override
{
Widget::render(r);
exception::SDLError::success_or_throw(
SDL_RenderSetViewport(r, &_viewport),
__FILE__,
__LINE__
);
// TODO: smoothstep
for (int i = _border_width; i > 0; i--)
{
exception::SDLError::success_or_throw(SDL_SetRenderDrawColor(
r,
color_border.r / i,
color_border.g / i,
color_border.b / i,
color_border.a
),
__FILE__,
__LINE__
);
SDL_Rect border_area = {
_input_area.x - i,
_input_area.y - i,
_input_area.w + 2 * i,
_input_area.h + 2 * i
};
exception::SDLError::success_or_throw(
SDL_RenderDrawRect(r, &border_area),
__FILE__,
__LINE__
);
}
if (MouseHandler::_is_hovered || FocusHandler::_has_focus)
_input_caption.color_bg = color_bg_focused;
else
_input_caption.color_bg = color_bg;
_input_caption.render(r);
}
T value_from_string(std::string s)
{
T v;
@ -235,15 +222,6 @@ namespace bwidgets::abstract
return s;
}
virtual void viewport(const SDL_Rect& vp) noexcept override
{
Widget::viewport(vp);
_input_caption.viewport(
utils::math::rect_offset(_input_area, _viewport)
);
}
};
}

View File

@ -4,6 +4,11 @@
#include <vector>
#include <basic_widgets/abstract/widget.hpp>
#include <basic_widgets/sdl/data_type.hpp>
namespace bwidgets::sdl {
class Renderer;
}
namespace bwidgets::abstract
{
@ -12,19 +17,20 @@ namespace bwidgets::abstract
protected:
std::vector<Widget*> _widgets;
virtual void _update_layout() noexcept = 0;
virtual void _handle_geometry_change(const SDL_Rect&) noexcept override;
virtual void _handle_renderer_change(sdl::Renderer*) override;
virtual void _handle_rendering() override;
virtual void _update_layout(const SDL_Rect&) noexcept = 0;
public:
Widget::Size margins {4, 4};
sdl::Size margins {4, 4};
virtual ~Layout() noexcept;
virtual void add_widget(Widget*);
virtual void handle_event(const SDL_Event&) override;
virtual void render(SDL_Renderer*) override;
virtual void viewport(const SDL_Rect&) noexcept override;
virtual void handle_event(const SDL_Event&) override;
virtual Widget::Size size() const noexcept override = 0;
virtual sdl::Size size() const noexcept override = 0;
};
}

View File

@ -0,0 +1,20 @@
#ifndef TEXTURE_HANDLER_
#define TEXTURE_HANDLER_
namespace bwidgets::sdl {
class Renderer;
}
namespace bwidgets::abstract
{
class Texture_Handler
{
protected:
virtual void _handle_texture_update(sdl::Renderer*) = 0;
public:
virtual ~Texture_Handler() noexcept {};
};
}
#endif

View File

@ -1,11 +1,12 @@
#ifndef WIDGET_HPP
#define WIDGET_HPP
#include <functional>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_rect.h>
#include <basic_widgets/sdl/data_type.hpp>
#include <basic_widgets/sdl/renderer.hpp>
struct SDL_Renderer;
namespace bwidgets::abstract
@ -13,30 +14,43 @@ namespace bwidgets::abstract
class Widget
{
protected:
SDL_Rect _viewport {0, 0, 0, 0};
SDL_Rect _widget_area {0, 0, 0, 0};
sdl::Renderer* _renderer {nullptr};
SDL_Rect _viewport {0, 0, 0, 0};
SDL_Rect _widget_area {0, 0, 0, 0};
virtual void _update_widget_area() noexcept;
virtual void _handle_geometry_change(const SDL_Rect&) noexcept = 0;
virtual void _handle_renderer_change(sdl::Renderer*) {}
virtual void _handle_rendering() = 0;
public:
struct Size {
int w;
int h;
};
Widget* parent;
Widget(Widget* parent=nullptr);
virtual ~Widget() noexcept;
Widget(Widget* p=nullptr) : parent(p) {}
//*********************************************
//
virtual ~Widget() noexcept = default;
virtual void handle_event(const SDL_Event&);
virtual void render(SDL_Renderer*);
virtual void viewport(const SDL_Rect&) noexcept;
inline virtual Size size() const noexcept
// -----
inline virtual sdl::Size size() const noexcept
{
return {_widget_area.w, _widget_area.h};
}
//
//*********************************************
virtual void render() final;
// -----
virtual inline void renderer(sdl::Renderer* r) final
{
_handle_renderer_change(r);
_renderer = r;
}
virtual inline void viewport(const SDL_Rect& vp) noexcept final
{
_handle_geometry_change(vp);
_viewport = vp;
}
};
}

View File

@ -3,8 +3,9 @@
#include <string>
#include <basic_widgets/abstract/mouse_handler.hpp>
#include <basic_widgets/caption.hpp>
#include <basic_widgets/abstract/mouse_handler.hpp>
#include <basic_widgets/sdl/data_type.hpp>
namespace bwidgets::widget
{
@ -17,8 +18,10 @@ namespace bwidgets::widget
SDL_Rect _caption_area;
SDL_Color _color_foreground {0, 0, 0, SDL_ALPHA_OPAQUE};
void _handle_font_change() override;
void _update_widget_area() noexcept override;
virtual void _handle_font_change() override;
virtual void _handle_geometry_change(const SDL_Rect&) noexcept override;
virtual void _handle_renderer_change(sdl::Renderer*) override;
virtual void _handle_rendering() override;
public:
int border_width {3};
@ -27,10 +30,9 @@ namespace bwidgets::widget
Button(Widget* parent=nullptr);
virtual void render(SDL_Renderer*) override;
Widget::Size size() const noexcept override;
virtual const std::string& text() const noexcept;
virtual void text(std::string) noexcept;
virtual sdl::Size size() const noexcept override;
virtual const std::string& text() const noexcept;
virtual void text(std::string);
};
}

View File

@ -4,14 +4,20 @@
#include <string>
#include <basic_widgets/abstract/font_handler.hpp>
#include <basic_widgets/abstract/texture_handler.hpp>
#include <basic_widgets/abstract/widget.hpp>
#include <basic_widgets/sdl/data_type.hpp>
struct SDL_Surface;
struct SDL_Texture;
namespace bwidgets::sdl {
class Texture;
}
namespace bwidgets::widget
{
class Caption : public abstract::Widget, public abstract::FontHandler
class Caption : public abstract::Widget, public abstract::FontHandler,
public abstract::Texture_Handler
{
public:
enum struct AlignmentH {
@ -27,30 +33,32 @@ namespace bwidgets::widget
};
private:
SDL_Color _color_fg {0, 0, 0, SDL_ALPHA_OPAQUE};
std::string _text;
SDL_Surface* _text_surface {nullptr};
SDL_Texture* _text_texture {nullptr};
SDL_Renderer* _renderer {nullptr};
SDL_Color _color_fg {0, 0, 0, SDL_ALPHA_OPAQUE};
std::string _text;
SDL_Surface* _text_surface {nullptr};
sdl::Texture* _text_texture {nullptr};
void _handle_font_change() override;
void _render_text();
virtual void _handle_font_change() override;
virtual void _handle_geometry_change(const SDL_Rect&) noexcept override;
virtual void _handle_renderer_change(sdl::Renderer*) override;
virtual void _handle_rendering() override;
virtual void _handle_texture_update(sdl::Renderer*) override;
virtual void _render_text();
public:
struct {
AlignmentH h;
AlignmentV v;
} alignment {AlignmentH::LEFT, AlignmentV::CENTER};
SDL_Color color_bg {0, 0, 0, SDL_ALPHA_TRANSPARENT};
abstract::Widget::Size margins {3, 0};
} alignment {AlignmentH::LEFT, AlignmentV::CENTER};
SDL_Color color_bg {0, 0, 0, SDL_ALPHA_TRANSPARENT};
sdl::Size margins {2, 2};
Caption(Widget* parent=nullptr);
Caption(Widget* parent=nullptr) : Widget(parent) {}
~Caption() noexcept;
virtual void color_fg(const SDL_Color&);
virtual void render(SDL_Renderer*) override;
virtual Widget::Size size() const noexcept override;
virtual const std::string& text() const noexcept;
virtual sdl::Size size() const noexcept override;
virtual const std::string& text() const noexcept;
virtual void text(const std::string&);
};
}

View File

@ -0,0 +1,65 @@
#ifndef EXCEPTION_HPP
#define EXCEPTION_HPP
#include <any>
#include <concepts>
#include <exception>
#include <map>
#include <string>
extern "C" {
const char* SDL_GetError();
}
namespace bwidgets::exception
{
struct Base : std::exception
{
typedef std::map<const char*, const std::any> Args;
const Args args;
const char* file;
const char* func;
const int line;
const char* what;
Base(const char* file, const char* func, int l, Args a, const char* w)
: args(a), file(file), func(func), line(l), what(w) {}
};
struct FontconfigError final : Base
{
FontconfigError(const char* file, const char* func, const int l, Args a={}, const char* w=nullptr)
: Base(file, func, l, a, w) {}
};
struct SDLError final : Base
{
SDLError(const char* file, const char* func, const int l, Args a={}, const char* w=SDL_GetError())
: Base(file, func, l, a, w) {}
static inline int
success_or_throw(int code, const char* file, const char* func, const int l, Args a={}, const char* w=SDL_GetError())
{
if (code < 0) throw SDLError(file, func, l, a, w);
return code;
}
};
template<typename T>
concept Exception = std::derived_from<T, Base>;
template<Exception E, typename T>
static inline
T* ptr_or_throw(T* ptr, const char* file, const char* func, const int l, Base::Args a={}, const char* w=nullptr)
{
if constexpr (std::same_as<E, SDLError>)
if (w == nullptr)
w = SDL_GetError();
if (ptr == nullptr) throw E(file, func, l, a, w);
return ptr;
}
}
#endif

View File

@ -8,10 +8,10 @@ namespace bwidgets::widget
class Horizontal_Layout final : public abstract::Layout
{
private:
void _update_layout() noexcept override;
void _update_layout(const SDL_Rect&) noexcept override;
public:
Widget::Size size() const noexcept override;
sdl::Size size() const noexcept override;
};
}

View File

@ -35,9 +35,9 @@ namespace bwidgets::widget
abstract::Input<T>::_handle_font_change();
}
virtual void _update_widget_area() noexcept override
virtual void _handle_geometry_change(const SDL_Rect& vp) noexcept override
{
abstract::Input<T>::_update_widget_area();
abstract::Input<T>::_handle_geometry_change(vp);
const int widest_button =
_increment_button.size().w
@ -45,25 +45,56 @@ namespace bwidgets::widget
: _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;
const int field_width = vp.w - 2 * button_area_width;
abstract::Input<T>::_input_area.x =
abstract::Widget::_widget_area.x =
abstract::Widget::_widget_area.x + button_area_width;
abstract::Input<T>::_input_area.w = field_width;
abstract::Widget::_widget_area.w = field_width;
abstract::Input<T>::_input_caption.viewport(
utils::math::rect_offset(
utils::math::rect_margin(
abstract::Widget::_widget_area,
{abstract::Input<T>::_border_width,
abstract::Input<T>::_border_width}
),
vp
)
);
_decrement_button_area = {
spacing,
utils::math::center_rect(abstract::Widget::_viewport.h, _decrement_button.size().h),
abstract::Widget::_widget_area.y,
_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),
vp.w - spacing - _increment_button.size().w,
abstract::Widget::_widget_area.y,
_increment_button.size().w,
_increment_button.size().h
};
_increment_button.viewport(utils::math::rect_offset(
_increment_button_area, vp
));
_decrement_button.viewport(utils::math::rect_offset(
_decrement_button_area, vp
));
}
virtual void _handle_renderer_change(sdl::Renderer* r)
{
abstract::Input<T>::_handle_renderer_change(r);
_decrement_button.renderer(r);
_increment_button.renderer(r);
}
virtual void _handle_rendering() override
{
abstract::Input<T>::_handle_rendering();
_increment_button.render();
_decrement_button.render();
}
public:
@ -141,13 +172,6 @@ namespace bwidgets::widget
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;
@ -160,18 +184,6 @@ namespace bwidgets::widget
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
));
}
};
}

View File

@ -0,0 +1,40 @@
#ifndef C_DATA_TYPE_HPP
#define C_DATA_TYPE_HPP
#include <concepts>
namespace bwidgets::abstract {
class Widget;
}
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;
namespace bwidgets::sdl
{
struct Size
{
int w = 0; int h = 0;
inline Size operator-() {
return {-w, -h};
}
template<Numeric T> inline Size operator*(T a) {
return {a * w, a * h};
}
};
template<typename T> struct DataType
{
friend abstract::Widget;
public:
T* c_pod;
DataType(T* ptr=nullptr) : c_pod(ptr) {}
virtual ~DataType<T>() noexcept {};
};
}
#endif

View File

@ -0,0 +1,77 @@
#ifndef SDL_RENDERER_HPP
#define SDL_RENDERER_HPP
#include <cstdint>
#include <vector>
#include <SDL2/SDL_render.h>
#include <basic_widgets/exception.hpp>
#include <basic_widgets/sdl/data_type.hpp>
namespace bwidgets::sdl
{
class Texture;
class Renderer final : public DataType<SDL_Renderer>
{
private:
static inline SDL_RendererInfo _info(SDL_Renderer* r)
{
SDL_RendererInfo info;
exception::SDLError::success_or_throw(SDL_GetRendererInfo(r, &info),
__FILE__, __FUNCTION__, __LINE__);
return info;
}
public:
Renderer(SDL_Renderer*);
Renderer(SDL_Window*, int, uint32_t);
~Renderer() override;
const SDL_RendererInfo info;
SDL_BlendMode blend_mode();
Renderer& blend_mode(SDL_BlendMode);
Renderer& clear();
Renderer& copy(Texture&, const SDL_Rect*, const SDL_Rect*);
SDL_Color draw_color();
Renderer& draw_color(const SDL_Color&);
Renderer& draw_line(const SDL_Point&, const SDL_Point&);
Renderer& draw_lines(const std::vector<SDL_Point>);
Renderer& draw_point(const SDL_Point&);
Renderer& draw_points(const std::vector<SDL_Point>);
Renderer& draw_rect(const SDL_Rect&);
Renderer& draw_rects(const std::vector<SDL_Rect>);
Renderer& fill_rect(const SDL_Rect&);
Renderer& fill_rects(const std::vector<SDL_Rect>);
Size output_size();
void present();
SDL_Rect viewport();
Renderer& viewport(const SDL_Rect*);
inline Renderer& copy(Texture& t, const SDL_Rect* src, const SDL_Rect& dst)
{
auto d = dst;
return copy(t, src, &d);
}
inline Renderer& copy(Texture& t, const SDL_Rect& src, const SDL_Rect* dst)
{
auto s = src;
return copy(t, &s, dst);
}
inline Renderer& copy(Texture& t, const SDL_Rect& src, const SDL_Rect& dst)
{
auto s = src, d = dst;
return copy(t, &s, &d);
}
inline Renderer& viewport(const SDL_Rect& vp)
{
auto v = vp;
return viewport(&v);
}
};
}
#endif

View File

@ -0,0 +1,77 @@
#ifndef SDL_TEXTURE_HPP
#define SDL_TEXTURE_HPP
#include <cstdint>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_pixels.h>
#include <basic_widgets/exception.hpp>
#include <basic_widgets/sdl/data_type.hpp>
namespace bwidgets::sdl
{
class Renderer;
class Texture final : public DataType<SDL_Texture>
{
struct Attr
{
uint32_t format_raw;
SDL_PixelFormat* format;
SDL_TextureAccess access;
int w, h;
} _attributes;
public:
Texture(SDL_Texture*);
Texture(Renderer* r,
SDL_PixelFormatEnum f,
SDL_TextureAccess a,
int w, int h);
Texture(Renderer*, SDL_Surface*);
~Texture() noexcept override;
uint8_t alpha_mode();
Texture& alpha_mode(uint8_t);
SDL_BlendMode blend_mode();
Texture& blend_mode(SDL_BlendMode);
SDL_Color color_mode();
Texture& color_mode(const SDL_Color&);
SDL_ScaleMode scale_mode();
Texture& scale_mode(SDL_ScaleMode);
Texture& update(SDL_Rect*, const void*, int);
inline const Attr& attributes()
{
return _attributes;
}
inline Texture& update(const SDL_Rect& r, const void* pix, int pitch)
{
SDL_Rect rect = r;
update(&rect, pix, pitch);
return *this;
}
static inline const Attr attributes(SDL_Texture* t)
{
Attr attr {};
exception::SDLError::success_or_throw(
SDL_QueryTexture(t,
&attr.format_raw, (int*)&attr.access, &attr.w, &attr.h),
__FILE__, __FUNCTION__, __LINE__
);
attr.format = exception::ptr_or_throw<exception::SDLError>(
SDL_AllocFormat(attr.format_raw),
__FILE__, __FUNCTION__, __LINE__
);
return attr;
}
};
}
#endif

View File

@ -5,6 +5,8 @@
#include <SDL2/SDL_rect.h>
#include <basic_widgets/sdl/data_type.hpp>
namespace bwidgets::utils::math
{
struct RectOverflow : std::exception {};
@ -34,6 +36,16 @@ namespace bwidgets::utils::math
SDL_PointInRect(&bottom_right, &outer);
}
static inline SDL_Rect rect_margin(const SDL_Rect& r, const sdl::Size& margin) noexcept
{
return {
r.x + margin.w,
r.y + margin.h,
r.w - 2 * margin.w,
r.h - 2 * margin.h
};
}
static inline SDL_Rect rect_offset(const SDL_Rect& r, const SDL_Rect& offset) noexcept
{
return {

View File

@ -8,6 +8,7 @@
#include <SDL2/SDL_render.h>
#include <basic_widgets/exception.hpp>
#include <basic_widgets/sdl/texture.hpp>
struct SDL_Texture;
@ -15,12 +16,12 @@ namespace bwidgets::utils::render
{
SDL_Color aa(const SDL_Color&&, const int, const float);
SDL_Texture* filled_circle(const SDL_Color&,
sdl::Texture filled_circle(const SDL_Color&,
int resolution,
SDL_Renderer*,
int aa_pixels = 3);
void set_pixels_color(SDL_Texture*, std::function<Uint32 (const SDL_Point&,
const SDL_PixelFormat*)>);
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)
{

View File

@ -8,10 +8,10 @@ namespace bwidgets::widget
class Vertical_Layout final : public abstract::Layout
{
private:
void _update_layout() noexcept override;
void _update_layout(const SDL_Rect&) noexcept override;
public:
Widget::Size size() const noexcept override;
sdl::Size size() const noexcept override;
};
}

View File

@ -1,6 +1,6 @@
project('sdl2_basic_widgets', 'cpp',
version : '0.1pre',
default_options : ['warning_level=3', 'cpp_std=c++20'],
default_options : ['warning_level=3', 'cpp_std=c++20', 'd_ndebug=if-release'],
license: 'EUPL-1.2')
sdl = [
@ -22,6 +22,8 @@ libbasic_widgets = static_library('basic_widgets',
'src/mouse_handler.cpp',
'src/vertical_layout.cpp',
'src/widget.cpp',
'src/sdl/renderer.cpp',
'src/sdl/texture.cpp',
'src/utils/render.cpp',
'src/utils/font.cpp',
dependencies : [sdl, fontconfig],

View File

@ -3,6 +3,7 @@
#include <basic_widgets/button.hpp>
#include <basic_widgets/exception.hpp>
#include <basic_widgets/utils/math.hpp>
#include <cstdint>
using namespace bwidgets;
using namespace bwidgets::utils;
@ -11,71 +12,13 @@ using exception::SDLError;
widget::Button::Button(Widget* parent)
: abstract::Widget(parent),
_caption(parent)
_caption(this)
{
_focus_area = _click_area = &_widget_area;
_caption.alignment.h = Caption::AlignmentH::CENTER;
}
void widget::Button::render(SDL_Renderer* r)
{
Widget::render(r);
SDL_RenderSetViewport(r, &_viewport);
// TODO: smoothstep it
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 = _is_hovered ? color_bg_hover : color_bg;
if (_is_pushed)
{
for (int i = 0; i < border_width; i++)
{
SDL_SetRenderDrawColor(
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 = {
_widget_area.x + i,
_widget_area.y + i,
_widget_area.w - i * 2,
_widget_area.h - i * 2
};
SDL_RenderFillRect(r, &rect);
}
}
else
{
for (int i = border_width; i >= 0; i--)
{
SDL_SetRenderDrawColor(
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;
SDL_Rect rect = {
_widget_area.x + margin,
_widget_area.y + margin,
_widget_area.w - margin * 2,
_widget_area.h - margin * 2
};
SDL_RenderFillRect(r, &rect);
}
}
_caption.render(r);
}
abstract::Widget::Size widget::Button::size() const noexcept
sdl::Size widget::Button::size() const noexcept
{
return {
_caption.size().w + 2 * border_width,
@ -88,41 +31,101 @@ const std::string& widget::Button::text() const noexcept
return _caption.text();
}
void widget::Button::text(std::string txt) noexcept
void widget::Button::text(std::string txt)
{
_caption.text(txt);
_update_widget_area();
_handle_geometry_change(_viewport);
}
void widget::Button::_handle_font_change()
{
_caption.font(_font);
_update_widget_area();
_handle_geometry_change(_viewport);
}
void widget::Button::_update_widget_area() noexcept
void widget::Button::_handle_geometry_change(const SDL_Rect& vp) noexcept
{
int h = _caption.size().h + 2 * border_width;
_widget_area = {
0,
math::center_rect(_viewport.h, _caption.size().h) - border_width,
_viewport.w,
math::center_rect(vp.h, _caption.size().h) - border_width,
vp.w,
h
};
auto txt_size = _caption.size();
_caption_area = {
math::center_rect(_viewport.w, txt_size.w),
math::center_rect(_viewport.h, txt_size.h),
math::center_rect(vp.w, txt_size.w),
math::center_rect(vp.h, txt_size.h),
txt_size.w,
txt_size.h
};
_caption.viewport({
_viewport.x + _caption_area.x,
_viewport.y + _caption_area.y,
vp.x + _caption_area.x,
vp.y + _caption_area.y,
_caption_area.w,
_caption_area.h
});
}
void widget::Button::_handle_renderer_change(sdl::Renderer* r)
{
_caption.renderer(r);
}
void widget::Button::_handle_rendering()
{
// TODO: smoothstep it
auto color_shade = [](int base, int step, int total_steps) -> uint8_t {
int steps_to_max = 255 - base;
return base + step * steps_to_max / total_steps;
};
SDL_Color c = _is_hovered ? color_bg_hover : color_bg;
if (_is_pushed)
{
for (int i = 0; i < border_width; i++)
{
_renderer->draw_color({
color_shade(c.r, i, border_width),
color_shade(c.g, i, border_width),
color_shade(c.b, i, border_width),
c.a
})
.draw_rect({
_widget_area.x + i,
_widget_area.y + i,
_widget_area.w - i * 2,
_widget_area.h - i * 2
});
}
}
else
{
for (int i = border_width; i >= 0; i--)
{
int margin = border_width - i;
_renderer->draw_color({
color_shade(c.r, i, border_width),
color_shade(c.g, i, border_width),
color_shade(c.b, i, border_width),
c.a
}).draw_rect({
_widget_area.x + margin,
_widget_area.y + margin,
_widget_area.w - margin * 2,
_widget_area.h - margin * 2
});
}
}
_renderer->draw_color(c).fill_rect({
_widget_area.x + border_width,
_widget_area.y + border_width,
_widget_area.w - 2 * border_width,
_widget_area.h - 2 * border_width
});
_caption.render();
}

View File

@ -2,21 +2,17 @@
#include <SDL2/SDL_ttf.h>
#include <basic_widgets/caption.hpp>
#include <basic_widgets/sdl/renderer.hpp>
#include <basic_widgets/sdl/texture.hpp>
#include <basic_widgets/utils/math.hpp>
#include <iostream>
using namespace bwidgets;
using namespace bwidgets::utils;
widget::Caption::Caption(abstract::Widget* parent)
: abstract::Widget(parent)
{
}
widget::Caption::~Caption() noexcept
{
SDL_FreeSurface(_text_surface);
SDL_DestroyTexture(_text_texture);
if (_text_texture != nullptr) delete _text_texture;
}
void widget::Caption::color_fg(const SDL_Color& c)
@ -25,74 +21,15 @@ void widget::Caption::color_fg(const SDL_Color& c)
_render_text();
}
void widget::Caption::render(SDL_Renderer* r)
{
Widget::render(r);
SDL_RenderSetViewport(r, &_viewport);
SDL_SetRenderDrawColor(r, color_bg.r, color_bg.g, color_bg.b, color_bg.a);
SDL_RenderFillRect(r, &_widget_area);
if (_text_surface == nullptr)
return;
if (_text_texture == nullptr || _renderer != r)
{
_renderer = r;
_text_texture = SDL_CreateTextureFromSurface(r, _text_surface);
}
abstract::Widget::Size size_dst {
(int)((float)_text_surface->w / _text_surface->h * _viewport.h),
_viewport.h
};
SDL_Rect texture_dst {
margins.w,
utils::math::center_rect(_viewport.h, size_dst.h),
size_dst.w,
size_dst.h
};
switch (alignment.h)
{
case AlignmentH::CENTER:
texture_dst.x = utils::math::center_rect(_viewport.w, texture_dst.w);
break;
case AlignmentH::LEFT:
break;
case AlignmentH::RIGHT:
texture_dst.x = _viewport.w - texture_dst.w - margins.w;
break;
}
switch (alignment.v)
{
case AlignmentV::BOTTOM:
texture_dst.y = _viewport.h - texture_dst.h - margins.h;
break;
case AlignmentV::CENTER:
break;
case AlignmentV::TOP:
texture_dst.y = margins.h;
}
SDL_RenderCopy(
r,
_text_texture,
NULL,
&texture_dst
);
}
abstract::Widget::Size widget::Caption::size() const noexcept
sdl::Size widget::Caption::size() const noexcept
{
if (_font == nullptr)
return {-1, -1};
abstract::Widget::Size size;
sdl::Size size;
TTF_SizeUTF8(_font, _text.c_str(), &size.w, &size.h);
size = {size.w + margins.w, size.h + margins.h};
return size;
// 2 * margins?
return {size.w + 2 * margins.w, size.h + 2 * margins.h};
}
const std::string& widget::Caption::text() const noexcept
@ -111,6 +48,72 @@ void widget::Caption::_handle_font_change()
_render_text();
}
void widget::Caption::_handle_geometry_change(const SDL_Rect& vp) noexcept
{
_widget_area = {margins.w, margins.h, vp.w - 2 * margins.w, vp.h - 2 * margins.h};
}
void widget::Caption::_handle_renderer_change(sdl::Renderer* r)
{
_handle_texture_update(r);
}
void widget::Caption::_handle_rendering()
{
_renderer->draw_color(color_bg);
_renderer->fill_rect(utils::math::rect_margin(_widget_area, -margins * 2));
if (_text_surface == nullptr)
return;
sdl::Size size_dst {
(int)((float)_text_surface->w / _text_surface->h * _widget_area.h),
_widget_area.h
};
SDL_Rect texture_dst {
margins.w,
utils::math::center_rect(_widget_area.h, size_dst.h) + margins.h,
size_dst.w,
size_dst.h
};
switch (alignment.h)
{
case AlignmentH::CENTER:
texture_dst.x = utils::math::center_rect(_widget_area.w, texture_dst.w)
+ _widget_area.x;
break;
case AlignmentH::LEFT:
break;
case AlignmentH::RIGHT:
texture_dst.x = _widget_area.w - texture_dst.w - margins.w + _widget_area.x;
break;
}
switch (alignment.v)
{
case AlignmentV::BOTTOM:
texture_dst.y = _widget_area.h - texture_dst.h - margins.h + _widget_area.y;
break;
case AlignmentV::CENTER:
break;
case AlignmentV::TOP:
texture_dst.y = margins.h + _widget_area.y;
}
_renderer->copy(*_text_texture, NULL, texture_dst);
}
void widget::Caption::_handle_texture_update(sdl::Renderer* r)
{
if (r != nullptr && _text_surface != nullptr)
{
if (_text_texture != nullptr)
{
delete _text_texture;
_text_texture = nullptr;
}
_text_texture = new sdl::Texture(r, _text_surface);
}
}
void widget::Caption::_render_text()
{
if (_font == nullptr)
@ -122,8 +125,5 @@ void widget::Caption::_render_text()
_text.c_str(),
_color_fg
);
SDL_DestroyTexture(_text_texture);
_text_texture = nullptr;
_update_widget_area();
_handle_texture_update(_renderer);
}

View File

@ -2,9 +2,9 @@
using namespace bwidgets;
abstract::Widget::Size widget::Horizontal_Layout::size() const noexcept
sdl::Size widget::Horizontal_Layout::size() const noexcept
{
abstract::Widget::Size max {0, 0};
sdl::Size max {0, 0};
for (const auto* w : _widgets)
{
@ -20,18 +20,18 @@ abstract::Widget::Size widget::Horizontal_Layout::size() const noexcept
};
}
void widget::Horizontal_Layout::_update_layout() noexcept
void widget::Horizontal_Layout::_update_layout(const SDL_Rect& vp) noexcept
{
int widget_size = (_viewport.w - (_widgets.size() + 1) * margins.w)
int widget_size = (vp.w - (_widgets.size() + 1) * margins.w)
/ _widgets.size();
for (std::vector<Widget*>::size_type i = 0; i < _widgets.size(); i++)
{
_widgets.at(i)->viewport({
_viewport.x + margins.w + (int)i * (widget_size + margins.w),
_viewport.y + margins.h,
vp.x + margins.w + (int)i * (widget_size + margins.w),
vp.y + margins.h,
widget_size - margins.w,
_viewport.h - margins.h
vp.h - margins.h
});
}
}

View File

@ -21,16 +21,19 @@ void abstract::Layout::handle_event(const SDL_Event& ev)
widget_ptr->handle_event(ev);
}
void abstract::Layout::render(SDL_Renderer* r)
void abstract::Layout::_handle_geometry_change(const SDL_Rect& vp) noexcept
{
SDL_RenderSetViewport(r, &_viewport);
_update_layout(vp);
}
void abstract::Layout::_handle_renderer_change(sdl::Renderer* r)
{
for (auto* widget : _widgets)
widget->renderer(r);
}
void abstract::Layout::_handle_rendering()
{
for (Widget* widget_ptr : _widgets)
widget_ptr->render(r);
}
void abstract::Layout::viewport(const SDL_Rect& vp) noexcept
{
Widget::viewport(vp);
_update_layout();
widget_ptr->render();
}

187
src/sdl/renderer.cpp Normal file
View File

@ -0,0 +1,187 @@
#include <string>
#include <basic_widgets/sdl/renderer.hpp>
#include <basic_widgets/sdl/texture.hpp>
using namespace bwidgets;
using exception::SDLError;
using exception::ptr_or_throw;
using std::vector;
sdl::Renderer::Renderer(SDL_Renderer* r)
: DataType(r),
info(_info(r))
{
}
sdl::Renderer::Renderer(SDL_Window* w, int index, uint32_t flags=0)
: DataType(ptr_or_throw<SDLError>(SDL_CreateRenderer(w, index, flags),
__FILE__, __FUNCTION__, __LINE__,
exception::Base::Args({
{"w", std::to_string((size_t)w)},
{"index", std::to_string(index)},
{"flags", std::to_string(flags)}
}))),
info(_info(c_pod))
{
}
sdl::Renderer::~Renderer()
{
SDL_DestroyRenderer(c_pod);
}
SDL_BlendMode sdl::Renderer::blend_mode()
{
SDL_BlendMode mode;
SDLError::success_or_throw(SDL_GetRenderDrawBlendMode(c_pod, &mode),
__FILE__, __FUNCTION__, __LINE__);
return mode;
}
sdl::Renderer& sdl::Renderer::blend_mode(SDL_BlendMode mode)
{
SDLError::success_or_throw(SDL_SetRenderDrawBlendMode(c_pod, mode),
__FILE__, __FUNCTION__, __LINE__);
return *this;
}
sdl::Renderer& sdl::Renderer::clear()
{
SDLError::success_or_throw(SDL_RenderClear(c_pod),
__FILE__, __FUNCTION__, __LINE__);
return *this;
}
sdl::Renderer& sdl::Renderer::copy(sdl::Texture& t,
const SDL_Rect* src, const SDL_Rect* dst)
{
SDLError::success_or_throw(
SDL_RenderCopy(c_pod, t.c_pod, src, dst),
__FILE__, __FUNCTION__, __LINE__
);
return *this;
}
SDL_Color sdl::Renderer::draw_color()
{
SDL_Color c;
SDLError::success_or_throw(
SDL_GetRenderDrawColor(c_pod, &c.r, &c.g, &c.b, &c.a),
__FILE__, __FUNCTION__, __LINE__
);
return c;
}
sdl::Renderer& sdl::Renderer::draw_color(const SDL_Color& c)
{
SDLError::success_or_throw(
SDL_SetRenderDrawColor(c_pod, c.r, c.g, c.b, c.a),
__FILE__, __FUNCTION__, __LINE__
);
return *this;
}
sdl::Renderer& sdl::Renderer::draw_line(const SDL_Point& a, const SDL_Point& b)
{
SDLError::success_or_throw(SDL_RenderDrawLine(c_pod, a.x, a.y, b.x, b.y),
__FILE__, __FUNCTION__, __LINE__);
return *this;
}
sdl::Renderer& sdl::Renderer::draw_lines(vector<SDL_Point> pts)
{
SDLError::success_or_throw(SDL_RenderDrawLines(c_pod, pts.data(), pts.size()),
__FILE__, __FUNCTION__, __LINE__);
return *this;
}
sdl::Renderer& sdl::Renderer::draw_point(const SDL_Point& p)
{
SDLError::success_or_throw(SDL_RenderDrawPoint(c_pod, p.x, p.y),
__FILE__, __FUNCTION__, __LINE__);
return *this;
}
sdl::Renderer& sdl::Renderer::draw_points(const vector<SDL_Point> pts)
{
SDLError::success_or_throw(SDL_RenderDrawPoints(c_pod, pts.data(), pts.size()),
__FILE__, __FUNCTION__, __LINE__);
return *this;
}
sdl::Renderer& sdl::Renderer::draw_rect(const SDL_Rect& r)
{
const SDL_Rect rect = r;
SDLError::success_or_throw(SDL_RenderDrawRect(c_pod, &rect),
__FILE__, __FUNCTION__, __LINE__);
return *this;
}
sdl::Renderer& sdl::Renderer::draw_rects(const vector<SDL_Rect> rs)
{
SDLError::success_or_throw(SDL_RenderDrawRects(c_pod, rs.data(), rs.size()),
__FILE__, __FUNCTION__, __LINE__);
return *this;
}
sdl::Renderer& sdl::Renderer::fill_rect(const SDL_Rect& r)
{
SDL_Rect rect = r;
SDLError::success_or_throw(SDL_RenderFillRect(c_pod, &rect),
__FILE__, __FUNCTION__, __LINE__);
return *this;
}
sdl::Renderer& sdl::Renderer::fill_rects(const std::vector<SDL_Rect> rs)
{
SDLError::success_or_throw(SDL_RenderFillRects(c_pod, rs.data(), rs.size()),
__FILE__, __FUNCTION__, __LINE__);
return *this;
}
sdl::Size sdl::Renderer::output_size()
{
Size s;
SDLError::success_or_throw(SDL_GetRendererOutputSize(c_pod, &s.w, &s.h),
__FILE__, __FUNCTION__, __LINE__);
return s;
}
void sdl::Renderer::present()
{
SDL_RenderPresent(c_pod);
}
SDL_Rect sdl::Renderer::viewport()
{
SDL_Rect vp;
SDL_RenderGetViewport(c_pod, &vp);
return vp;
}
sdl::Renderer& sdl::Renderer::viewport(const SDL_Rect* vp)
{
SDLError::success_or_throw(SDL_RenderSetViewport(c_pod, vp),
__FILE__, __FUNCTION__, __LINE__);
return *this;
}

132
src/sdl/texture.cpp Normal file
View File

@ -0,0 +1,132 @@
#include <string>
#include <basic_widgets/sdl/renderer.hpp>
#include <basic_widgets/sdl/texture.hpp>
#include "to_string.hpp"
using namespace bwidgets;
using exception::Base;
using exception::SDLError;
using exception::ptr_or_throw;
sdl::Texture::Texture(SDL_Texture* t)
: DataType(t)
{
_attributes = attributes(t);
}
sdl::Texture::Texture(
Renderer* r,
SDL_PixelFormatEnum f,
SDL_TextureAccess a,
int w, int h
)
: Texture(ptr_or_throw<SDLError>(SDL_CreateTexture(r->c_pod, f, a, w, h),
__FILE__, __FUNCTION__, __LINE__,
Base::Args({{"r", std::to_string((size_t)r)},
{"f", SDL_GetPixelFormatName(f)},
{"a", to_string(a)},
{"w", std::to_string(w)}, {"h", std::to_string(h)}})))
{
}
sdl::Texture::Texture(Renderer* r, SDL_Surface* s)
: DataType(ptr_or_throw<SDLError>(SDL_CreateTextureFromSurface(r->c_pod, s),
__FILE__, __FUNCTION__, __LINE__,
Base::Args({{"r", std::to_string((size_t)r)},
{"s", std::to_string((size_t)s)}})))
{
_attributes = attributes(c_pod);
}
sdl::Texture::~Texture() noexcept
{
SDL_DestroyTexture(c_pod);
}
uint8_t sdl::Texture::alpha_mode()
{
uint8_t mode;
SDLError::success_or_throw(SDL_GetTextureAlphaMod(c_pod, &mode),
__FILE__, __FUNCTION__, __LINE__);
return mode;
}
sdl::Texture& sdl::Texture::alpha_mode(uint8_t m)
{
SDLError::success_or_throw(SDL_SetTextureAlphaMod(c_pod, m),
__FILE__, __FUNCTION__, __LINE__,
Base::Args({{"m", std::to_string(m)}}));
return *this;
}
SDL_BlendMode sdl::Texture::blend_mode()
{
SDL_BlendMode mode;
SDLError::success_or_throw(SDL_GetTextureBlendMode(c_pod, &mode),
__FILE__, __FUNCTION__, __LINE__);
return mode;
}
sdl::Texture& sdl::Texture::blend_mode(SDL_BlendMode m)
{
SDLError::success_or_throw(SDL_SetTextureBlendMode(c_pod, m),
__FILE__, __FUNCTION__, __LINE__,
Base::Args({{"m", to_string(m)}}));
return *this;
}
SDL_Color sdl::Texture::color_mode()
{
SDL_Color mode;
SDLError::success_or_throw(
SDL_GetTextureColorMod(c_pod, &mode.r, &mode.g, &mode.b),
__FILE__, __FUNCTION__, __LINE__
);
return mode;
}
sdl::Texture& sdl::Texture::color_mode(const SDL_Color& m)
{
SDLError::success_or_throw(SDL_SetTextureColorMod(c_pod, m.r, m.g, m.b),
__FILE__, __FUNCTION__, __LINE__, Base::Args({{"m", to_string(m)}}));
return *this;
}
SDL_ScaleMode sdl::Texture::scale_mode()
{
SDL_ScaleMode mode;
SDLError::success_or_throw(SDL_GetTextureScaleMode(c_pod, &mode),
__FILE__, __FUNCTION__, __LINE__);
return mode;
}
sdl::Texture& sdl::Texture::scale_mode(SDL_ScaleMode m)
{
SDLError::success_or_throw(SDL_SetTextureScaleMode(c_pod, m),
__FILE__, __FUNCTION__, __LINE__, Base::Args({{"m", to_string(m)}}));
return *this;
}
sdl::Texture& sdl::Texture::update(SDL_Rect* r, const void* pixels, int pitch)
{
SDLError::success_or_throw(SDL_UpdateTexture(c_pod, r, pixels, pitch),
__FILE__, __FUNCTION__, __LINE__,
Base::Args({
{"r", r },
{"pixels", std::to_string((size_t)pixels)},
{"pitch", std::to_string(pitch)}
}));
return *this;
}

66
src/sdl/to_string.hpp Normal file
View File

@ -0,0 +1,66 @@
#ifndef SDL_TO_STRING_HPP
#define SDL_TO_STRING_HPP
#include <string>
#include <SDL2/SDL_render.h>
namespace bwidgets::sdl
{
static inline const char* to_string(SDL_BlendMode m)
{
switch (m)
{
case SDL_BLENDMODE_ADD:
return "add";
case SDL_BLENDMODE_BLEND:
return "blend";
case SDL_BLENDMODE_MOD:
return "mod";
case SDL_BLENDMODE_MUL:
return "mul";
case SDL_BLENDMODE_NONE:
return "none";
[[unlikely]] default:
return "unknown";
}
}
static inline const std::string to_string(const SDL_Color& c)
{
return "r:" + std::to_string(c.r) + "g:" + std::to_string(c.g)
+ "b:" + std::to_string(c.b) + "a:" + std::to_string(c.a);
}
static inline const char* to_string(SDL_ScaleMode m)
{
switch (m)
{
case SDL_ScaleModeBest:
return "best";
case SDL_ScaleModeLinear:
return "linear";
case SDL_ScaleModeNearest:
return "nearest";
[[unlikely]] default:
return "unknown";
}
}
static inline const char* to_string(SDL_TextureAccess a)
{
switch (a)
{
case SDL_TEXTUREACCESS_STATIC:
return "static";
case SDL_TEXTUREACCESS_STREAMING:
return "streaming";
case SDL_TEXTUREACCESS_TARGET:
return "target";
[[unlikely]] default:
return "unknown";
}
}
}
#endif

View File

@ -30,18 +30,18 @@ namespace bwidgets::utils::font
};
FcConfig* conf = ptr_or_throw<FontconfigError>(FcInitLoadConfigAndFonts(),
__FILE__, __LINE__, "init failed");
__FILE__, __FUNCTION__, __LINE__, exception::Base::Args(), "init failed");
ptrs.push_back(conf);
FcPattern* pattern;
try
{
pattern = ptr_or_throw<FontconfigError>(FcNameParse((FcChar8*)pat.c_str()),
__FILE__, __LINE__, "pattern parsing failed");
__FILE__, __FUNCTION__, __LINE__, exception::Base::Args(), "pattern parsing failed");
ptrs.push_back(pattern);
if(FcConfigSubstitute(conf, pattern, FcMatchPattern) == FcFalse)
throw exception::FontconfigError {__FILE__, __LINE__, "FcConfigSubstitute failed"};
throw exception::FontconfigError {__FILE__, __FUNCTION__, __LINE__, exception::Base::Args(), "FcConfigSubstitute failed"};
}
catch (const std::exception& e)
{
@ -67,7 +67,7 @@ namespace bwidgets::utils::font
clean(ptrs.size());
if (file_path.empty())
throw exception::FontconfigError {__FILE__, __LINE__, "no font found"};
throw exception::FontconfigError {__FILE__, __FUNCTION__, __LINE__, exception::Base::Args(), "no font found"};
return file_path;
}

View File

@ -16,86 +16,67 @@ namespace bwidgets::utils::render
const float d
)
noexcept
{
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};
{
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)
};
}
return {
base_color.r, base_color.g, base_color.b,
static_cast<Uint8>(base_color.a * factor)
};
}
SDL_Texture* filled_circle(
sdl::Texture filled_circle(
const SDL_Color& c,
const int resolution,
SDL_Renderer* r,
sdl::Renderer* r,
const int aa_pixels
)
{
auto texture {SDL_CreateTexture(
r,
sdl::Texture texture (r,
SDL_PIXELFORMAT_RGBA32,
SDL_TEXTUREACCESS_STATIC,
resolution, resolution)};
resolution, resolution);
const auto radius {resolution / 2};
const SDL_Point center {radius, radius};
ptr_or_throw(texture, __FILE__, __LINE__);
try
{
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);
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);
}
);
SDLError::success_or_throw(SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND),
__FILE__, __LINE__);
SDLError::success_or_throw(SDL_SetTextureScaleMode(texture, SDL_ScaleModeNearest),
__FILE__, __LINE__);
}
catch (const std::exception& e)
{
SDL_DestroyTexture(texture);
throw e;
}
return SDL_MapRGBA(format,
aa_color.r, aa_color.g, aa_color.b,
aa_color.a);
}
);
texture.blend_mode(SDL_BLENDMODE_BLEND);
texture.scale_mode(SDL_ScaleModeNearest);
return texture;
}
void set_pixels_color(
SDL_Texture* t,
sdl::Texture& t,
std::function<Uint32 (const SDL_Point&, const SDL_PixelFormat*)> pixel_color
)
{
int w, h;
Uint32 format_enum;
SDLError::success_or_throw(SDL_QueryTexture(t, &format_enum, NULL, &w, &h),
__FILE__, __LINE__);
auto format = ptr_or_throw(SDL_AllocFormat(format_enum), __FILE__, __LINE__);
auto pitch = w * format->BytesPerPixel;
auto attr = t.attributes();
auto pitch = attr.w * attr.format->BytesPerPixel;
std::vector<Uint32> pixels;
pixels.reserve(h * pitch);
for (auto y = 0; y < h; y++)
pixels.reserve(attr.h * pitch);
for (auto y = 0; y < attr.h; y++)
{
for (auto x = 0; x < w; x++)
for (auto x = 0; x < attr.w; x++)
{
pixels.push_back(pixel_color({x, y}, format));
pixels.push_back(pixel_color({x, y}, attr.format));
}
}
SDL_FreeFormat(format);
SDLError::success_or_throw(SDL_UpdateTexture(t, NULL, (const void*)pixels.data(), pitch), __FILE__, __LINE__);
t.update(nullptr, (const void*)pixels.data(), pitch);
}
}

View File

@ -2,9 +2,9 @@
using namespace bwidgets;
abstract::Widget::Size widget::Vertical_Layout::size() const noexcept
sdl::Size widget::Vertical_Layout::size() const noexcept
{
abstract::Widget::Size max {0, 0};
sdl::Size max {0, 0};
for (const auto* w : _widgets)
{
@ -20,17 +20,17 @@ abstract::Widget::Size widget::Vertical_Layout::size() const noexcept
};
}
void widget::Vertical_Layout::_update_layout() noexcept
void widget::Vertical_Layout::_update_layout(const SDL_Rect& vp) noexcept
{
int widget_size = (_viewport.h - (_widgets.size() + 1) * margins.h)
int widget_size = (vp.h - (_widgets.size() + 1) * margins.h)
/ _widgets.size();
for (std::vector<Widget*>::size_type i = 0; i < _widgets.size(); i++)
{
SDL_Rect widget_vp = {
_viewport.x + margins.w,
_viewport.y + margins.h + (int)i * (widget_size + margins.h),
_viewport.w - margins.w,
vp.x + margins.w,
vp.y + margins.h + (int)i * (widget_size + margins.h),
vp.w - margins.w,
widget_size - margins.h,
};
_widgets.at(i)->viewport(widget_vp);

View File

@ -7,15 +7,6 @@
using namespace bwidgets;
abstract::Widget::Widget(Widget* p)
: parent(p)
{
}
abstract::Widget::~Widget() noexcept
{
}
void abstract::Widget::handle_event(const SDL_Event &ev)
{
if (auto handler = dynamic_cast<KeyboardHandler*>(this);
@ -49,30 +40,20 @@ void abstract::Widget::handle_event(const SDL_Event &ev)
}
}
void abstract::Widget::render(SDL_Renderer* r)
void abstract::Widget::render()
{
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_TRANSPARENT);
SDL_RenderDrawRect(r, &dbg_outline);
}
if (_renderer == nullptr)
return;
void abstract::Widget::viewport(const SDL_Rect& vp) noexcept
{
_viewport = vp;
_update_widget_area();
}
//#ifndef _NDEBUG
_renderer->viewport(nullptr)
.draw_color({0, 255, 0, SDL_ALPHA_TRANSPARENT})
.fill_rect({
_viewport.x - 1, _viewport.y - 1,
_viewport.w + 2, _viewport.h + 2
});
//#endif
void abstract::Widget::_update_widget_area() noexcept
{
_widget_area = {
0,
0,
_viewport.w,
_viewport.h
};
_renderer->viewport(_viewport);
_handle_rendering();
}