wip general code improvement

This commit is contained in:
Andrea Blankenstijn 2021-08-06 14:22:56 +02:00
parent 2d4c6e3694
commit cab6fa0cf4
48 changed files with 963 additions and 756 deletions

View File

@ -1,2 +1,2 @@
Checks: 'bugprone-*,cert-*,misc-*,modernize-*,performance-*,portability-*,readability-*,-misc-non-private-member-variables-in-classes,-bugprone-easily-swappable-parameters,-readability-braces-around-statements,-cppcoreguidelines-non-private-member-variables-in-classes,-readability-named-parameter'
Checks: 'bugprone-*,cert-*,misc-*,modernize-*,performance-*,portability-*,readability-*,-misc-non-private-member-variables-in-classes,-bugprone-easily-swappable-parameters,-readability-braces-around-statements,-cppcoreguidelines-non-private-member-variables-in-classes,-readability-named-parameter,-modernize-use-trailing-return-type'
FormatStyle: file

2
clang-format.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
find . -name "*.?pp" -print -exec clang-format -i {} \;

24
clang-tidy.py Executable file
View File

@ -0,0 +1,24 @@
#!/usr/bin/env python3
from concurrent.futures import ProcessPoolExecutor
from pathlib import Path, PurePath
from subprocess import CompletedProcess, PIPE, STDOUT, run
files = sorted(Path().rglob("*.?pp"))
def tidy(file: PurePath) -> CompletedProcess:
return run(
["clang-tidy", "-p", "build", "--use-color", "--quiet", file], stdout=PIPE, stderr=STDOUT,
check=False
)
def main():
with ProcessPoolExecutor() as executor:
for task_done in executor.map(tidy, files):
print(task_done.stdout.decode())
if __name__ == "__main__":
main()

View File

@ -4,11 +4,11 @@
#include "run.hpp"
using widget::Button;
using bwidgets::Button;
auto main() -> int
{
run_example<Button>([](Button* w, core::Font* f, int x, int y) {
run_example<Button>([](auto* w, auto* f, auto x, auto y) {
w->click_handler = [x, y](const SDL_MouseButtonEvent&) {
std::cout << "button(" << x << ',' << y << "):click!" << std::endl;
};

View File

@ -2,12 +2,12 @@
#include "run.hpp"
using widget::Caption;
using bwidgets::Caption;
auto main() -> int
{
run_example<Caption>(
[](Caption* w, core::Font* f, int, int) {
[](auto* w, auto* f, auto, auto) {
w->alignment = Caption::Alignment::CENTER;
w->text("¡jello!");
w->font(f);

View File

@ -3,7 +3,7 @@
auto main() -> int
{
run_example<Example>([](Example* w, core::Font*, int x, int y) {
run_example<Example>([](auto* w, auto*, auto x, auto y) {
w->cycle_r = (x + 1) * 3000; // NOLINT(readability-magic-numbers)
w->cycle_b = (y + 1) * 3000; // NOLINT(readability-magic-numbers)
w->cycle_b = (1 + x + y) * (y + 1) * 400; // NOLINT(readability-magic-numbers)

View File

@ -1,5 +1,5 @@
#ifndef BWIDGET_EXAMPLES_EXAMPLE_WIDGET_
#define BWIDGET_EXAMPLES_EXAMPLE_WIDGET_
#ifndef BWIDGETS_EXAMPLES_EXAMPLE_WIDGET
#define BWIDGETS_EXAMPLES_EXAMPLE_WIDGET
#include <SDL2/SDL_timer.h>
@ -7,9 +7,10 @@
#include <basic_widgets/core/renderer.hpp>
#include <basic_widgets/w/base/widget.hpp>
using bwidgets::core::rect_margin;
using bwidgets::core::Size;
using bwidgets::widget::Widget;
using bwidgets::Color;
using bwidgets::rect_margin;
using bwidgets::Size;
using bwidgets::Widget;
class Example final : public Widget
{
@ -21,16 +22,20 @@ private:
void _handle_rendering() override
{
auto now = SDL_GetTicks();
uint8_t r = 255 * (now % cycle_r / (float)cycle_r); // NOLINT
uint8_t g = 255 * (now % cycle_g / (float)cycle_g); // NOLINT
uint8_t b = 255 * (now % cycle_b / (float)cycle_b); // NOLINT
SDL_Color base_color {r, g, b, 255}; // NOLINT
auto now = SDL_GetTicks();
uint8_t r = 255 * (now % cycle_r / (float)cycle_r); // NOLINT
uint8_t g = 255 * (now % cycle_g / (float)cycle_g); // NOLINT
uint8_t b = 255 * (now % cycle_b / (float)cycle_b); // NOLINT
// NOLINTNEXTLINE(readability-magic-numbers)
Color base_color {r, g, b, 255};
int border = 10; // NOLINT
const int border = 10;
for (auto i = 0; i < border; i += 3) {
uint8_t alpha = 255 * i / border; // NOLINT
_renderer->draw_color({base_color.r, base_color.g, base_color.b, alpha})
uint8_t alpha = 255 * i / border; // NOLINT(readability-magic-numbers)
_renderer
->draw_color({
{base_color().r, base_color().g, base_color().b, alpha}
})
->draw_rect(rect_margin(_widget_area, {i, i}));
}
@ -40,13 +45,13 @@ private:
}
public:
unsigned int cycle_r {3500}; // NOLINT
unsigned int cycle_g {3500}; // NOLINT
unsigned int cycle_b {3500}; // NOLINT
unsigned int cycle_r {3500}; // NOLINT(readability-magic-numbers)
unsigned int cycle_g {3500}; // NOLINT(readability-magic-numbers)
unsigned int cycle_b {3500}; // NOLINT(readability-magic-numbers)
[[nodiscard]] auto size() const noexcept -> Size override
{
return {128, 64}; // NOLINT
return {128, 64}; // NOLINT(readability-magic-numbers)
}
};

View File

@ -2,15 +2,14 @@
#include "run.hpp"
using widget::NumericInput;
using bwidgets::NumericInput;
auto main() -> int
{
run_example<NumericInput<float>>(
[](NumericInput<float>* w, core::Font* f, int, int) {
w->font(f);
w->button_step = 0.5; // NOLINT(readability-magic-numbers)
w->value_range(-3.14, 8.5); // NOLINT(readability-magic-numbers)
});
run_example<NumericInput<float>>([](auto* w, auto* f, auto, auto) {
w->font(f);
w->button_step = 0.5; // NOLINT(readability-magic-numbers)
w->value_range(-3.14, 8.5); // NOLINT(readability-magic-numbers)
});
return 0;
}

View File

@ -1,46 +1,54 @@
#ifndef BWIDGETS_EXAMPLES_RUN_HPP_
#define BWIDGETS_EXAMPLES_RUN_HPP_
#ifndef BWIDGETS_EXAMPLES_RUN_HPP
#define BWIDGETS_EXAMPLES_RUN_HPP
#include <concepts>
#include <functional>
#include <iostream>
#include <stack>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <basic_widgets/core/font.hpp>
#include <basic_widgets/core/renderer.hpp>
#include <basic_widgets/w/aligned_layout.hpp>
#include <basic_widgets/w/base/widget.hpp>
#include <basic_widgets/w/horizontal_layout.hpp>
#include <basic_widgets/w/vertical_layout.hpp>
using namespace bwidgets;
template<typename T>
concept WidgetType = std::derived_from<T, widget::Widget>;
concept WidgetType = std::derived_from<T, bwidgets::Widget>;
template<WidgetType W>
void run_example(std::function<void(W*, core::Font*, int, int)> setup, int w = 3,
void run_example(std::function<void(W*, bwidgets::Font*, int, int)> setup, int w = 3,
int h = 3)
{
std::stack<std::function<void()>> cleaners;
try {
core::SDLError::success_or_throw(SDL_Init(SDL_INIT_VIDEO), __FILE__,
__FUNCTION__, __LINE__);
core::SDLError::success_or_throw(TTF_Init(), __FILE__, __FUNCTION__, __LINE__);
bwidgets::SDLError::success_or_throw(SDL_Init(SDL_INIT_VIDEO), __FILE__,
__FUNCTION__, __LINE__);
cleaners.emplace([]() { SDL_Quit(); });
bwidgets::SDLError::success_or_throw(TTF_Init(), __FILE__, __FUNCTION__,
__LINE__);
cleaners.emplace([]() { TTF_Quit(); });
const core::Size size_init {854, 480};
auto* font {new core::Font(core::Font::find("Monospace"), 16)}; // NOLINT
const bwidgets::Size size_init {854, 480};
auto* font {new bwidgets::Font(bwidgets::Font::find("Monospace"),
16)}; // NOLINT(readability-magic-numbers)
cleaners.emplace([font]() { delete font; });
auto* win {SDL_CreateWindow("basic_widgets example", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, size_init.w, size_init.h,
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE
| SDL_WINDOW_UTILITY)};
auto* renderer {new core::Renderer(win, -1, SDL_RENDERER_ACCELERATED)};
cleaners.emplace([win]() { SDL_DestroyWindow(win); });
auto* renderer {new bwidgets::Renderer(win, -1, SDL_RENDERER_ACCELERATED)};
cleaners.emplace([renderer]() { delete renderer; });
renderer->blend_mode(SDL_BLENDMODE_BLEND);
auto* layout {new widget::HorizontalLayout()};
auto* layout {
new bwidgets::AlignedLayout(bwidgets::AlignedLayout::Alignment::HORIZONTAL)};
cleaners.emplace([layout]() { delete layout; });
for (auto x = 0; x < w; x++) {
auto* col = new widget::VerticalLayout();
auto* col =
new bwidgets::AlignedLayout(bwidgets::AlignedLayout::Alignment::VERTICAL);
for (auto y = 0; y < h; y++) {
auto* widget = new W();
setup(widget, font, x, y);
@ -70,15 +78,18 @@ void run_example(std::function<void(W*, core::Font*, int, int)> setup, int w = 3
layout->handle_event(ev);
}
renderer->draw_color({50, 60, 70, SDL_ALPHA_OPAQUE}); // NOLINT
// NOLINTNEXTLINE(readability-magic-numbers)
renderer->draw_color({50, 60, 70, SDL_ALPHA_OPAQUE});
renderer->clear();
layout->render();
renderer->present();
}
} catch (const core::SDLError& e) {
while (!cleaners.empty()) cleaners.top()(), cleaners.pop();
} catch (const bwidgets::SDLError& e) {
std::cerr << "Error: " << e.file << ":" << e.func << ":" << e.line << ": "
<< e.what << std::endl;
while (!cleaners.empty()) cleaners.top()(), cleaners.pop();
}
}

View File

@ -1,24 +1,24 @@
#ifndef BWIDGETS_DRAW_HPP_
#define BWIDGETS_DRAW_HPP_
#ifndef BWIDGETS_DRAW_HPP
#define BWIDGETS_DRAW_HPP
#include <cstdint>
#include <functional>
struct SDL_Color;
struct SDL_PixelFormat;
struct SDL_Point;
namespace bwidgets::core
namespace bwidgets
{
struct Renderer;
struct Texture;
class Color;
class Renderer;
class Texture;
auto aa(const SDL_Color&, int, float) noexcept -> SDL_Color;
auto filled_circle(const SDL_Color&, int resolution, Renderer*, int aa_pixels = 3)
-> core::Texture*;
void set_pixels_color(
Texture*,
const std::function<uint32_t(const SDL_Point&, const SDL_PixelFormat*)>&);
[[nodiscard]] auto aa(const Color&, int, float) noexcept -> Color;
[[nodiscard]] auto filled_circle(const Color&, int resolution, Renderer*,
int aa_pixels = 3) -> Texture*;
void set_pixels_color(
Texture*,
const std::function<uint32_t(const SDL_Point&, const SDL_PixelFormat*)>&);
}
#endif

View File

@ -1,16 +1,15 @@
#ifndef BWIDGETS_FONT_HPP_
#define BWIDGETS_FONT_HPP_
#ifndef BWIDGETS_FONT_HPP
#define BWIDGETS_FONT_HPP
#include <string>
#include <SDL2/SDL_ttf.h>
#include <basic_widgets/core/type/color.hpp>
#include <basic_widgets/core/type/opaque_struct.hpp>
#include <basic_widgets/core/type/size.hpp>
/* struct SDL_Surface; */
namespace bwidgets::core
namespace bwidgets
{
struct Font final : OpaqueStruct<TTF_Font>
{
@ -29,16 +28,18 @@ namespace bwidgets::core
BLENDED
};
struct Style
enum struct Style
{
Style() = delete;
static const uint8_t BOLD = TTF_STYLE_BOLD, ITALIC = TTF_STYLE_ITALIC,
NORMAL = TTF_STYLE_NORMAL,
STRIKETHROUGH = TTF_STYLE_STRIKETHROUGH,
UNDERLINE = TTF_STYLE_UNDERLINE;
BOLD = TTF_STYLE_BOLD,
ITALIC = TTF_STYLE_ITALIC,
NORMAL = TTF_STYLE_NORMAL,
STRIKETHROUGH = TTF_STYLE_STRIKETHROUGH,
UNDERLINE = TTF_STYLE_UNDERLINE
};
inline static const Color default_color_bg {255, 255, 255, SDL_ALPHA_OPAQUE};
inline static const Color default_color_fg {0, 0, 0, SDL_ALPHA_OPAQUE};
const int ascent;
const int descent;
const long faces;
@ -53,27 +54,21 @@ namespace bwidgets::core
Font(const Font&) = delete;
~Font() override;
auto operator=(const Font) -> Font& = delete;
auto operator=(const Font&) -> Font& = delete;
auto hinting() -> Hinting;
auto hinting(Hinting) -> Font*;
auto kerning() -> bool;
auto kerning(bool) -> Font*;
auto outline() -> int;
auto outline(int) -> Font*;
auto render(RenderMode, const std::string&,
const SDL_Color& fg = {0, 0, 0,
255}, // NOLINT(readability-magic-numbers)
// NOLINTNEXTLINE(readability-magic-numbers)
const SDL_Color& bg = {255, 255, 255,
255}) // NOLINT(readability-magic-numbers)
-> SDL_Surface*;
auto style() -> uint8_t;
auto style(uint8_t) -> Font*;
auto text_size(const std::string&) -> Size;
[[nodiscard]] auto hinting() -> Hinting;
auto hinting(Hinting) -> Font*;
[[nodiscard]] auto kerning() -> bool;
auto kerning(bool) -> Font*;
[[nodiscard]] auto outline() -> int;
auto outline(int) -> Font*;
auto render(RenderMode, const std::string&, const Color& fg = default_color_fg,
const Color& bg = default_color_bg) -> SDL_Surface*;
[[nodiscard]] auto style() -> uint8_t;
auto style(uint8_t) -> Font*;
[[nodiscard]] auto text_size(const std::string&) -> Size;
static auto find(const std::string&) -> std::string;
[[nodiscard]] static auto find(const std::string&) -> std::string;
};
}

View File

@ -6,35 +6,51 @@
#include <SDL2/SDL_rect.h>
#include <basic_widgets/core/type/color.hpp>
#include <basic_widgets/core/type/concepts.hpp>
#include <basic_widgets/core/type/size.hpp>
namespace bwidgets::core
namespace bwidgets
{
static inline auto center_line(int available_len, int used_len) noexcept -> int
// NOLINTNEXTLINE(clang-diagnostic-unused-function)
[[nodiscard]] static inline auto center_line(int available_len, int used_len) noexcept -> int
{
return (available_len - used_len) / 2;
}
static inline auto distance_sqrd(const SDL_Point& a, const SDL_Point& b) noexcept
[[nodiscard]] static inline auto distance_sqrd(const SDL_Point& a, const SDL_Point& b) noexcept
-> float
{
return (a.x - b.x) * (a.x - b.x) // NOLINT(bugprone-narrowing-conversions)
// NOLINTNEXTLINE(bugprone-narrowing-conversions)
return (a.x - b.x) * (a.x - b.x)
+ (a.y - b.y) * (a.y - b.y);
}
static inline auto distance(const SDL_Point& a, const SDL_Point& b) noexcept -> float
// NOLINTNEXTLINE(clang-diagnostic-unused-function)
[[nodiscard]] static inline auto distance(const SDL_Point& a, const SDL_Point& b) noexcept -> float
{
return std::sqrt(distance_sqrd(a, b));
}
template<FloatingPoint F>
[[nodiscard]] static inline auto lerp(const Color& a, const Color& b, F x, bool op_alpha=false, bool op_color=true) -> Color
{
return {{
op_color ? (uint8_t)std::lerp(a().r, b().r, x) : a().r,
op_color ? (uint8_t)std::lerp(a().g, b().g, x) : a().g,
op_color ? (uint8_t)std::lerp(a().b, b().b, x) : a().b,
op_alpha ? (uint8_t)std::lerp(a().a, b().a, x) : a().a,
}};
}
template<Numeric N>
static inline auto linear(N x, N a, N b) noexcept -> float
[[nodiscard]] static inline auto linear(N x, N a, N b) noexcept -> float
{
return (float)(x - a) / (float)(b - a);
}
static inline auto rect_in_rect(const SDL_Rect outer, const SDL_Rect inner) noexcept
// NOLINTNEXTLINE(clang-diagnostic-unused-function)
[[nodiscard]] static inline auto rect_in_rect(const SDL_Rect outer, const SDL_Rect inner) noexcept
-> bool
{
SDL_Point top_left {inner.x, inner.y};
@ -44,26 +60,28 @@ namespace bwidgets::core
&& (SDL_PointInRect(&bottom_right, &outer) == SDL_TRUE);
}
static inline auto rect_margin(const SDL_Rect& r, const Size& margin) noexcept
// NOLINTNEXTLINE(clang-diagnostic-unused-function)
[[nodiscard]] static inline auto rect_margin(const SDL_Rect& r, const Size& margin) noexcept
-> SDL_Rect
{
return {r.x + margin.w, r.y + margin.h, r.w - 2 * margin.w, r.h - 2 * margin.h};
}
static inline auto rect_offset(const SDL_Rect& r, const SDL_Point& offset) noexcept
[[nodiscard]] static inline auto rect_offset(const SDL_Rect& r, const SDL_Point& offset) noexcept
-> SDL_Rect
{
return {r.x + offset.x, r.y + offset.y, r.w, r.h};
}
static inline auto rect_offset(const SDL_Rect& r, const SDL_Rect& offset) noexcept
// NOLINTNEXTLINE(clang-diagnostic-unused-function)
[[nodiscard]] static inline auto rect_offset(const SDL_Rect& r, const SDL_Rect& offset) noexcept
-> SDL_Rect
{
return rect_offset(r, SDL_Point {offset.x, offset.y});
}
template<Numeric N>
static inline auto smoothstep(N x, N a, N b) noexcept -> float
[[nodiscard]] static inline auto smoothstep(N x, N a, N b) noexcept -> float
{
const float x_norm = linear(std::clamp<float>(x, a, b), a, b);
return 3 * x_norm * x_norm - 2 * x_norm * x_norm * x_norm;

View File

@ -1,22 +1,22 @@
#ifndef BWIDGETS_RENDERER_HPP_
#define BWIDGETS_RENDERER_HPP_
#ifndef BWIDGETS_RENDERER_HPP
#define BWIDGETS_RENDERER_HPP
#include <cstdint>
#include <vector>
#include <SDL2/SDL_render.h>
#include <basic_widgets/core/type/color.hpp>
#include <basic_widgets/core/type/opaque_struct.hpp>
#include <basic_widgets/core/type/sdl_error.hpp>
#include <basic_widgets/core/type/size.hpp>
namespace bwidgets::core
namespace bwidgets
{
struct Texture;
class Texture;
struct Renderer final : OpaqueStruct<SDL_Renderer>
class Renderer final : public OpaqueStruct<SDL_Renderer>
{
private:
static inline auto _info(SDL_Renderer* r) -> SDL_RendererInfo
{
SDL_RendererInfo info;
@ -27,61 +27,61 @@ namespace bwidgets::core
}
public:
Renderer(SDL_Renderer*);
Renderer(SDL_Window*, int, uint32_t);
~Renderer() override;
const SDL_RendererInfo info;
auto blend_mode() -> SDL_BlendMode;
auto blend_mode(SDL_BlendMode) -> Renderer*;
auto clear() -> Renderer*;
auto copy(Texture*, const SDL_Rect*, const SDL_Rect*) -> Renderer*;
auto draw_color() -> SDL_Color;
auto draw_color(const SDL_Color&) -> Renderer*;
auto draw_line(const SDL_Point&, const SDL_Point&) -> Renderer*;
auto draw_lines(const std::vector<SDL_Point>&) -> Renderer*;
auto draw_point(const SDL_Point&) -> Renderer*;
auto draw_points(const std::vector<SDL_Point>&) -> Renderer*;
auto draw_rect(const SDL_Rect*) -> Renderer*;
auto draw_rects(const std::vector<SDL_Rect>&) -> Renderer*;
auto fill_rect(const SDL_Rect*) -> Renderer*;
auto fill_rects(const std::vector<SDL_Rect>&) -> Renderer*;
auto output_size() -> Size;
void present();
auto viewport() -> SDL_Rect;
auto viewport(const SDL_Rect*) -> Renderer*;
Renderer(SDL_Renderer*);
Renderer(SDL_Window*, int, uint32_t);
Renderer(const Renderer&) = delete;
~Renderer() override;
inline auto copy(Texture* t, const SDL_Rect* src, const SDL_Rect& dst)
-> Renderer*
auto operator=(const Renderer&) = delete;
[[nodiscard]] auto blend_mode() -> SDL_BlendMode;
auto blend_mode(SDL_BlendMode) -> Renderer*;
auto clear() -> Renderer*;
auto copy(Texture*, const SDL_Rect*, const SDL_Rect*) -> Renderer*;
[[nodiscard]] auto draw_color() -> Color;
auto draw_color(const Color&) -> Renderer*;
auto draw_line(const SDL_Point&, const SDL_Point&) -> Renderer*;
auto draw_lines(const std::vector<SDL_Point>&) -> Renderer*;
auto draw_point(const SDL_Point&) -> Renderer*;
auto draw_points(const std::vector<SDL_Point>&) -> Renderer*;
auto draw_rect(const SDL_Rect*) -> Renderer*;
auto draw_rects(const std::vector<SDL_Rect>&) -> Renderer*;
auto fill_rect(const SDL_Rect*) -> Renderer*;
auto fill_rects(const std::vector<SDL_Rect>&) -> Renderer*;
[[nodiscard]] auto output_size() -> Size;
void present();
[[nodiscard]] auto viewport() -> SDL_Rect;
auto viewport(const SDL_Rect*) -> Renderer*;
inline auto* copy(Texture* t, const SDL_Rect* src, const SDL_Rect& dst)
{
auto d = dst;
return copy(t, src, &d);
}
inline auto copy(Texture* t, const SDL_Rect& src, const SDL_Rect* dst)
-> Renderer*
inline auto* copy(Texture* t, const SDL_Rect& src, const SDL_Rect* dst)
{
auto s = src;
return copy(t, &s, dst);
}
inline auto copy(Texture* t, const SDL_Rect& src, const SDL_Rect& dst)
-> Renderer*
inline auto* copy(Texture* t, const SDL_Rect& src, const SDL_Rect& dst)
{
auto s = src;
auto d = dst;
return copy(t, &s, &d);
}
inline auto draw_rect(const SDL_Rect& r) -> Renderer*
inline auto* draw_rect(const SDL_Rect& r)
{
auto rect = r;
return draw_rect(&rect);
}
inline auto fill_rect(const SDL_Rect& r) -> Renderer*
inline auto* fill_rect(const SDL_Rect& r)
{
auto rect = r;
return fill_rect(&rect);
}
inline auto viewport(const SDL_Rect& vp) -> Renderer*
inline auto* viewport(const SDL_Rect& vp)
{
auto v = vp;
return viewport(&v);

View File

@ -1,19 +1,20 @@
#ifndef BWIDGETS_TEXTURE_HPP_
#define BWIDGETS_TEXTURE_HPP_
#ifndef BWIDGETS_TEXTURE_HPP
#define BWIDGETS_TEXTURE_HPP
#include <cstdint>
#include <SDL2/SDL_pixels.h>
#include <SDL2/SDL_render.h>
#include <basic_widgets/core/type/color.hpp>
#include <basic_widgets/core/type/opaque_struct.hpp>
#include <basic_widgets/core/type/sdl_error.hpp>
namespace bwidgets::core
namespace bwidgets
{
struct Renderer;
class Renderer;
struct Texture final : OpaqueStruct<SDL_Texture>
class Texture final : public OpaqueStruct<SDL_Texture>
{
struct Attr
{
@ -27,25 +28,27 @@ namespace bwidgets::core
Texture(SDL_Texture*);
Texture(Renderer* r, SDL_PixelFormatEnum f, SDL_TextureAccess a, int w, int h);
Texture(Renderer*, SDL_Surface*);
Texture(const Texture&) = delete;
~Texture() noexcept override;
auto alpha_mode() -> uint8_t;
auto alpha_mode(uint8_t) -> Texture*;
auto blend_mode() -> SDL_BlendMode;
auto blend_mode(SDL_BlendMode) -> Texture*;
auto color_mode() -> SDL_Color;
auto color_mode(const SDL_Color&) -> Texture*;
auto scale_mode() -> SDL_ScaleMode;
auto scale_mode(SDL_ScaleMode) -> Texture*;
auto update(SDL_Rect*, const void*, int) -> Texture*;
auto operator=(const Texture&) = delete;
[[nodiscard]] inline auto attributes() const -> const Attr&
[[nodiscard]] auto alpha_mode() -> uint8_t;
auto alpha_mode(uint8_t) -> Texture*;
[[nodiscard]] auto blend_mode() -> SDL_BlendMode;
auto blend_mode(SDL_BlendMode) -> Texture*;
[[nodiscard]] auto color_mode() -> Color;
auto color_mode(const Color&) -> Texture*;
[[nodiscard]] auto scale_mode() -> SDL_ScaleMode;
auto scale_mode(SDL_ScaleMode) -> Texture*;
auto update(SDL_Rect*, const void*, int) -> Texture*;
[[nodiscard]] inline const auto& attributes() const
{
return _attributes;
}
inline auto update(const SDL_Rect& r, const void* pix, int pitch) -> Texture*
inline auto update(const SDL_Rect& r, const void* pix, int pitch)
{
SDL_Rect rect = r;
update(&rect, pix, pitch);
@ -53,7 +56,7 @@ namespace bwidgets::core
return this;
}
static inline auto attributes(SDL_Texture* t) -> Attr
[[nodiscard]] static inline auto attributes(SDL_Texture* t)
{
Attr attr {};
SDLError::success_or_throw(SDL_QueryTexture(t, &attr.format_raw,

View File

@ -0,0 +1,136 @@
#ifndef BWIDGETS_COLOR_HPP
#define BWIDGETS_COLOR_HPP
#include <cstdint>
#include <functional>
#include <SDL2/SDL_assert.h>
#include <SDL2/SDL_pixels.h>
#include <basic_widgets/core/type/concepts.hpp>
namespace bwidgets
{
class Color final
{
public:
[[nodiscard]] inline auto& operator()()
{
return sdl_type;
}
[[nodiscard]] inline const auto& operator()() const
{
return sdl_type;
}
private:
bool _operate_on_alpha;
bool _operate_on_colors;
template<Numeric N>
inline auto _operate(const N& operand,
const std::function<N(const N&, const N&)>& operator_) const
{
Color c(sdl_type, _operate_on_alpha, _operate_on_colors);
if (_operate_on_alpha) {
auto a = std::round(operator_(c().a, operand));
SDL_assert_release(a == (uint8_t)a); // NOLINT
c().a = (uint8_t)a;
}
if (_operate_on_colors) {
auto r = std::round(operator_(c().r, operand));
auto g = std::round(operator_(c().g, operand));
auto b = std::round(operator_(c().b, operand));
// NOLINTNEXTLINE
SDL_assert_release(r == (uint8_t)r && g == (uint8_t)g
&& b == (uint8_t)b);
c().r = (uint8_t)r;
c().g = (uint8_t)g;
c().b = (uint8_t)b;
}
return c;
}
public:
SDL_Color sdl_type;
inline Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a,
bool op_on_alpha = false, bool op_on_colors = true) noexcept
: _operate_on_alpha(op_on_alpha),
_operate_on_colors(op_on_colors),
sdl_type({r, g, b, a})
{}
inline Color(const SDL_Color& c = {}, bool op_on_alpha = false,
bool op_on_colors = true) noexcept
: _operate_on_alpha(op_on_alpha), _operate_on_colors(op_on_colors), sdl_type(c)
{}
inline Color(const Color&) noexcept = default;
[[nodiscard]] inline auto alpha() const -> Color
{
return {sdl_type, _operate_on_colors, true};
}
[[nodiscard]] inline auto colors() const -> Color
{
return {sdl_type, true, _operate_on_alpha};
}
template<Numeric N>
[[nodiscard]] inline auto operator+(const N& operand) const
{
return _operate(operand, [](const N& a, const N& b) { return a + b; });
}
template<Numeric N>
[[nodiscard]] inline auto operator-(const N& operand) const
{
return _operate(operand, [](const N& a, const N& b) { return a - b; });
}
template<Numeric N>
[[nodiscard]] inline auto operator*(const N& operand) const
{
return _operate(operand, [](const N& a, const N& b) { return a * b; });
}
template<Numeric N>
[[nodiscard]] inline auto operator/(const N& operand) const
{
SDL_assert(operand != 0); // NOLINT
return _operate<N>(operand, [](const N& a, const N& b) { return a / b; });
}
inline auto& operator=(const Color& c)
{
if (this != &c) {
sdl_type = c.sdl_type;
}
return *this;
}
inline auto& operator=(const SDL_Color& c)
{
sdl_type = c;
return *this;
}
[[nodiscard]] inline auto operator==(const Color& other) const
{
return (_operate_on_colors && sdl_type.r == other().r
&& sdl_type.g == other().g && sdl_type.b == other().b)
|| (_operate_on_alpha && sdl_type.a == other().a);
}
[[nodiscard]] inline auto operator!=(const Color& other) const
{
return (sdl_type.r != other().r || sdl_type.g != other().g
|| sdl_type.b != other().b)
&& ((_operate_on_alpha && sdl_type.a != other().a)
|| !_operate_on_alpha);
}
};
}
#endif

View File

@ -6,7 +6,7 @@
#include <type_traits>
namespace bwidgets::core
namespace bwidgets
{
template<typename T>
concept CanToString = requires(T value)
@ -14,6 +14,9 @@ namespace bwidgets::core
std::to_string(value);
};
template<typename T>
concept FloatingPoint = std::is_floating_point_v<T>;
template<typename T>
concept Numeric = std::is_arithmetic_v<T>;

View File

@ -2,14 +2,13 @@
#define BWIDGETS_EXCEPTION_HPP
#include <any>
#include <cstdlib>
#include <exception>
#include <functional>
#include <map>
#include <basic_widgets/core/type/concepts.hpp>
namespace bwidgets::core
namespace bwidgets
{
struct BaseException : std::exception
{
@ -37,9 +36,8 @@ namespace bwidgets::core
template<Exception E, typename T>
static inline auto success_or_throw(
T code, const char* file, const char* func, const int l, const char* w = nullptr,
std::function<bool(const T&)> success = [](const T& code) {
return code == EXIT_SUCCESS;
}) -> T
std::function<bool(const T&)> success = [](const T& code) { return code == 0; })
-> T
{
if (!success(code)) throw E(file, func, l, w);
return code;

View File

@ -1,9 +1,9 @@
#ifndef BWIDGETS_FC_ERROR_HPP_
#define BWIDGETS_FC_ERROR_HPP_
#ifndef BWIDGETS_FC_ERROR_HPP
#define BWIDGETS_FC_ERROR_HPP
#include <basic_widgets/core/type/exception.hpp>
namespace bwidgets::core
namespace bwidgets
{
struct FCError final : BaseException
{

View File

@ -1,15 +1,23 @@
#ifndef BWIDGETS_OPAQUE_STRUCT_HPP
#define BWIDGETS_OPAQUE_STRUCT_HPP
namespace bwidgets::core
namespace bwidgets
{
template<typename T>
struct OpaqueStruct
class OpaqueStruct
{
T* c_pod;
protected:
T* _c_pod;
OpaqueStruct(T* ptr = nullptr) : c_pod(ptr) {}
public:
OpaqueStruct(T* ptr = nullptr) : _c_pod(ptr) {}
virtual ~OpaqueStruct<T>() noexcept = default;
// NOLINTNEXTLINE(modernize-use-trailing-return-type)
[[nodiscard]] inline auto& operator()()
{
return _c_pod;
}
};
template<typename... Ts>

View File

@ -1,5 +1,5 @@
#ifndef BWIDGETS_SDL_ERROR_HPP_
#define BWIDGETS_SDL_ERROR_HPP_
#ifndef BWIDGETS_SDL_ERROR_HPP
#define BWIDGETS_SDL_ERROR_HPP
#include <basic_widgets/core/type/exception.hpp>
@ -7,7 +7,7 @@ extern "C" {
auto SDL_GetError() -> const char*;
}
namespace bwidgets::core
namespace bwidgets
{
struct SDLError final : BaseException
{
@ -17,11 +17,12 @@ namespace bwidgets::core
{}
template<typename T>
static inline auto ptr_or_throw(T* ptr, const char* file, const char* func,
const int l, const char* w = nullptr) -> T*
[[nodiscard]] static inline auto ptr_or_throw(T* ptr, const char* file,
const char* func, const int l,
const char* w = nullptr) -> T*
{
if (ptr == nullptr) {
if (w == nullptr) w = SDL_GetError();
if (!ptr) {
if (!w) w = SDL_GetError();
throw SDLError(file, func, l, w);
}
return ptr;

View File

@ -1,30 +1,30 @@
#ifndef BWIDGETS_SIZE_HPP_
#define BWIDGETS_SIZE_HPP_
#ifndef BWIDGETS_SIZE_HPP
#define BWIDGETS_SIZE_HPP
#include <basic_widgets/core/type/concepts.hpp>
namespace bwidgets::core
namespace bwidgets
{
struct Size
{
int w;
int h;
inline auto operator-() const -> Size
[[nodiscard]] inline auto operator-() const -> Size
{
return {-w, -h};
}
inline auto operator+(const Size& s) const -> Size
[[nodiscard]] inline auto operator+(const Size& s) const -> Size
{
return {w + s.w, h + s.h};
}
inline auto operator-(const Size& s) const -> Size
[[nodiscard]] inline auto operator-(const Size& s) const -> Size
{
return {w - s.w, h - s.h};
}
template<Numeric T>
inline auto operator*(T a) -> Size
template<Numeric N>
[[nodiscard]] inline auto operator*(N a) const -> Size
{
return {a * w, a * h};
}

View File

@ -0,0 +1,26 @@
#ifndef BWIDGETS_ALIGNED_LAYOUT_HPP
#define BWIDGETS_ALIGNED_LAYOUT_HPP
#include <basic_widgets/w/base/layout.hpp>
namespace bwidgets
{
class AlignedLayout final : public Layout
{
private:
void _update_layout(const SDL_Rect&) noexcept override;
public:
enum struct Alignment
{
HORIZONTAL,
VERTICAL
} alignment;
AlignedLayout(Alignment align) : alignment(align) {}
[[nodiscard]] auto size() const noexcept -> Size override;
};
}
#endif

View File

@ -1,6 +1,7 @@
#ifndef BWIDGETS_INPUT_HPP_
#define BWIDGETS_INPUT_HPP_
#ifndef BWIDGETS_INPUT_HPP
#define BWIDGETS_INPUT_HPP
#include <cmath>
#include <exception>
#include <iomanip>
#include <sstream>
@ -15,7 +16,7 @@
#include <basic_widgets/w/feat/keyboard_handler.hpp>
#include <basic_widgets/w/feat/mouse_handler.hpp>
namespace bwidgets::widget
namespace bwidgets
{
template<typename T>
@ -25,10 +26,9 @@ namespace bwidgets::widget
public MouseHandler
{
protected:
const int _border_width {3};
Caption _input_caption;
Caption _input_caption;
Input(Widget* parent = nullptr) : Widget(parent)
Input(Widget* parent = nullptr) : Widget(parent), _input_caption(this)
{
FocusHandler::_focus_area = &_widget_area;
MouseHandler::_click_area = &_widget_area;
@ -47,24 +47,24 @@ namespace bwidgets::widget
}
}
void _handle_font_change(core::Font* f) override
void _handle_font_change(Font* f) override
{
_input_caption.font(f);
_handle_geometry_change(_viewport);
}
void _handle_font_color_change(const SDL_Color& fg, const SDL_Color& bg) override
void _handle_font_color_change(const Color& fg, const Color& bg) override
{
_input_caption.color_bg(bg)->color_fg(fg);
_input_caption.font_color_bg(bg)->font_color_fg(fg);
}
void _handle_geometry_change(const SDL_Rect& vp) noexcept override
{
const auto input_h = _input_caption.size().h + 2 * _border_width;
_widget_area = {0, core::center_line(vp.h, input_h), vp.w, input_h};
const auto input_h = _input_caption.size().h + 2 * border_width;
_widget_area = {0, center_line(vp.h, input_h), vp.w, input_h};
_input_caption.viewport(core::rect_offset(
{core::rect_margin(_widget_area, {_border_width, _border_width})}, vp));
_input_caption.viewport(rect_offset(
{rect_margin(_widget_area, {border_width, border_width})}, vp));
}
void _handle_key(const SDL_KeyboardEvent& key) override
@ -89,26 +89,24 @@ namespace bwidgets::widget
}
}
void _handle_renderer_change(core::Renderer* r) override
void _handle_renderer_change(Renderer* r) override
{
_input_caption.renderer(r);
}
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});
for (int i = border_width - 1; i >= 0; i--) {
const auto factor = linear(i, 0, border_width);
const auto& color_end = _has_focus ? color_bg_focused : color_bg;
const auto color_start = color_end / 2;
_renderer->draw_color(lerp(color_start, color_end, factor))
->draw_rect(rect_margin(_widget_area, {i, i}));
}
if (MouseHandler::_is_hovered || FocusHandler::_has_focus)
_input_caption.color_bg(color_bg_focused);
else _input_caption.color_bg(color_bg);
_input_caption.font_color_bg(color_bg_focused);
else _input_caption.font_color_bg(color_bg);
_input_caption.render();
}
@ -121,20 +119,21 @@ namespace bwidgets::widget
}
public:
SDL_Color color_border {160, 160, 160, // NOLINT(readability-magic-numbers)
SDL_ALPHA_OPAQUE};
SDL_Color color_bg {200, 200, 200, // NOLINT(readability-magic-numbers)
SDL_ALPHA_OPAQUE};
SDL_Color color_bg_focused {
255, 255, 255, SDL_ALPHA_OPAQUE}; // NOLINT(readability-magic-numbers)
int float_precision {2};
int input_min_width {1};
char input_width_unit {'W'};
T value {};
static const int border_width = 3;
inline static const Color default_color_bg = {200, 200, 200, SDL_ALPHA_OPAQUE};
inline static const Color default_color_bg_focused = {255, 255, 255,
SDL_ALPHA_OPAQUE};
virtual auto color_fg(const SDL_Color& c) noexcept -> Input<T>*
Color color_bg = default_color_bg;
Color color_bg_focused = default_color_bg_focused;
int float_precision = 2;
int input_min_width = 1;
char input_width_unit = 'W';
T value {};
virtual auto color_fg(const Color& c) noexcept -> Input<T>*
{
_input_caption.color_fg(c);
_input_caption.font_color_fg(c);
return this;
}
@ -149,27 +148,29 @@ namespace bwidgets::widget
return this;
}
[[nodiscard]] virtual auto
is_valid_input(const std::string& /*unused*/) const noexcept -> bool
[[nodiscard]] virtual auto is_valid_input(const std::string&) const noexcept
-> bool
{
return true;
}
virtual auto process_value(T x) const noexcept -> T
[[nodiscard]] virtual auto process_value(T x) const noexcept -> T
{
return x;
}
[[nodiscard]] inline auto size() const noexcept -> core::Size override
[[nodiscard]] inline auto size() const noexcept -> Size override
{
if (_font == nullptr) return {0, 0};
return {
_font->text_size(std::string(input_min_width, input_width_unit)).w
+ 2 * _border_width,
_font->line_skip + 4 * _border_width // _why_ 4 and not 2?…
+ 2 * border_width,
_font->line_skip + 4 * border_width // _why_ 4 and not 2?…
};
}
auto value_from_string(std::string s) -> T
[[nodiscard]] auto value_from_string(std::string s)
{
T v;
@ -182,33 +183,33 @@ namespace bwidgets::widget
T, std::string> || std::convertible_to<std::string, T>)
v = s;
else
static_assert((bool)sizeof(T)
&& false, // NOLINT(readability-simplify-boolean-expr)
// NOLINTNEXTLINE(readability-simplify-boolean-expr)
static_assert((bool)sizeof(T) && false,
"string cannot be converted to v type T.");
return process_value(v);
}
auto value_to_string(T value) -> std::string
[[nodiscard]] auto value_to_string(T value)
{
std::string s;
if constexpr (std::is_same_v<std::string,
T> || std::convertible_to<T, std::string>)
s = std::move(value);
else if constexpr (core::CanToString<T>) {
else if constexpr (CanToString<T>) {
std::stringstream ss;
ss << std::fixed << std::setprecision(float_precision) << value;
s = std::move(ss).str();
}
else if constexpr (core::Printable<T>) {
else if constexpr (Printable<T>) {
std::stringstream ss;
ss << value;
s = std::move(ss).str();
}
else
static_assert((bool)sizeof(T)
&& false, // NOLINT(readability-simplify-boolean-expr)
// NOLINTNEXTLINE(readability-simplify-boolean-expr)
static_assert((bool)sizeof(T) && false,
"value cannot be converted to string.");
return s;

View File

@ -1,5 +1,5 @@
#ifndef BWIDGETS_LAYOUT_HPP_
#define BWIDGETS_LAYOUT_HPP_
#ifndef BWIDGETS_LAYOUT_HPP
#define BWIDGETS_LAYOUT_HPP
#include <functional>
#include <vector>
@ -7,34 +7,36 @@
#include <basic_widgets/core/type/size.hpp>
#include <basic_widgets/w/base/widget.hpp>
namespace bwidgets::core
namespace bwidgets
{
struct Renderer;
class Renderer;
}
namespace bwidgets::widget
namespace bwidgets
{
class Layout : public Widget
{
protected:
std::vector<Widget*> _widgets;
void _handle_geometry_change(const SDL_Rect& /*unused*/) noexcept override;
void _handle_renderer_change(core::Renderer* /*unused*/) override;
void _handle_geometry_change(const SDL_Rect&) noexcept override;
void _handle_renderer_change(Renderer*) override;
void _handle_rendering() override;
virtual void _update_layout(const SDL_Rect&) noexcept = 0;
public:
core::Size margins {8, 8}; // NOLINT(readability-magic-numbers)
inline static const Size default_margins {8, 8};
Size margins = default_margins;
~Layout() noexcept override;
auto handle_event(const SDL_Event&) -> Layout* override;
[[nodiscard]] auto size() const noexcept -> Size override = 0;
virtual auto add_widget(Widget*) -> Layout*;
virtual void for_widgets(const std::function<void(Widget*)>&);
auto handle_event(const SDL_Event&) // NOLINT(readability-named-parameter)
-> Layout* override;
[[nodiscard]] auto size() const noexcept -> core::Size override = 0;
};
}

View File

@ -1,5 +1,5 @@
#ifndef BWIDGETS_WIDGET_HPP_
#define BWIDGETS_WIDGET_HPP_
#ifndef BWIDGETS_WIDGET_HPP
#define BWIDGETS_WIDGET_HPP
#include <basic_widgets/core/renderer.hpp>
#include <basic_widgets/core/type/size.hpp>
@ -8,17 +8,17 @@ union SDL_Event;
struct SDL_Renderer;
namespace bwidgets::widget
namespace bwidgets
{
class Widget
{
protected:
core::Renderer* _renderer {nullptr};
SDL_Rect _viewport {0, 0, 0, 0};
SDL_Rect _widget_area {0, 0, 0, 0};
Renderer* _renderer {nullptr};
SDL_Rect _viewport {0, 0, 0, 0};
SDL_Rect _widget_area {0, 0, 0, 0};
virtual void _handle_geometry_change(const SDL_Rect&) noexcept = 0;
virtual void _handle_renderer_change(core::Renderer* /*unused*/) {}
virtual void _handle_renderer_change(Renderer*) {}
virtual void _handle_rendering() = 0;
public:
@ -27,30 +27,13 @@ namespace bwidgets::widget
Widget(Widget* p = nullptr) : parent(p) {}
virtual ~Widget() noexcept = default;
virtual auto handle_event(const SDL_Event&) -> Widget*;
[[nodiscard]] inline virtual auto size() const noexcept -> core::Size = 0;
virtual auto render() -> Widget* final;
virtual inline auto renderer(core::Renderer* r) -> Widget* final
{
_handle_renderer_change(r);
_renderer = r;
return this;
}
[[nodiscard]] virtual inline auto viewport() const noexcept
-> const SDL_Rect& final
{
return _viewport;
}
virtual inline auto viewport(const SDL_Rect& vp) noexcept -> Widget* final
{
_handle_geometry_change(vp);
_viewport = vp;
return this;
}
virtual auto handle_event(const SDL_Event&) -> Widget*;
[[nodiscard]] virtual auto size() const noexcept -> Size = 0;
virtual auto render() -> Widget* final;
virtual auto renderer(Renderer*) -> Widget* final;
virtual auto viewport(const SDL_Rect&) noexcept -> Widget* final;
[[nodiscard]] virtual auto viewport() const noexcept -> const SDL_Rect& final;
};
}

View File

@ -1,44 +1,48 @@
#ifndef BWIDGETS_BUTTON_HPP_
#define BWIDGETS_BUTTON_HPP_
#ifndef BWIDGETS_BUTTON_HPP
#define BWIDGETS_BUTTON_HPP
#include <functional>
#include <string>
#include <basic_widgets/core/type/color.hpp>
#include <basic_widgets/w/caption.hpp>
#include <basic_widgets/w/feat/mouse_handler.hpp>
namespace bwidgets::widget
namespace bwidgets
{
class Button : public Widget,
public FontHandler,
public MouseHandler
{
protected:
Caption _caption;
SDL_Rect _caption_area {};
SDL_Color _color_foreground {0, 0, 0, SDL_ALPHA_OPAQUE};
Caption _caption;
SDL_Rect _caption_area {};
Color _color_foreground = default_color_fg;
void _handle_focus_change(bool /*unused*/) override {}
void _handle_font_change(core::Font* /*unused*/) override;
void _handle_font_color_change(const SDL_Color& /*unused*/,
const SDL_Color& /*unused*/) override;
void _handle_geometry_change(const SDL_Rect& /*unused*/) noexcept override;
void _handle_renderer_change(core::Renderer* /*unused*/) override;
void _handle_focus_change(bool) override {}
void _handle_font_change(Font*) override;
void _handle_font_color_change(const Color&, const Color&) override;
void _handle_geometry_change(const SDL_Rect&) noexcept override;
void _handle_renderer_change(Renderer*) override;
void _handle_rendering() override;
void _on_push(bool) override;
public:
std::function<SDL_Color(int, int)> border_gradient;
std::function<SDL_Color(int, int)> border_gradient_pushed;
core::Size border_size {3, 3};
SDL_Color color_bg {150, 150, 150, // NOLINT(readability-magic-numbers)
SDL_ALPHA_OPAQUE};
SDL_Color color_bg_hover {175, 175, 175, // NOLINT(readability-magic-numbers)
SDL_ALPHA_OPAQUE};
inline static const Color default_color_bg {150, 150, 150, SDL_ALPHA_OPAQUE};
inline static const Color default_color_bg_hover {175, 175, 175,
SDL_ALPHA_OPAQUE};
inline static const Color default_color_fg {0, 0, 0, SDL_ALPHA_OPAQUE};
std::function<Color(int, int)> border_gradient;
std::function<Color(int, int)> border_gradient_pushed;
Size border_size {3, 3};
Color color_bg = default_color_bg;
Color color_bg_hover = default_color_bg_hover;
Button(Widget* parent = nullptr);
[[nodiscard]] auto size() const noexcept -> core::Size override;
[[nodiscard]] auto size() const noexcept -> Size override;
[[nodiscard]] virtual auto text() const noexcept -> const std::string&;
virtual auto text(const std::string&) -> Button*;
};

View File

@ -1,5 +1,5 @@
#ifndef BWIDGETS_CAPTION_HPP_
#define BWIDGETS_CAPTION_HPP_
#ifndef BWIDGETS_CAPTION_HPP
#define BWIDGETS_CAPTION_HPP
#include <basic_widgets/core/texture.hpp>
#include <basic_widgets/core/type/size.hpp>
@ -9,12 +9,24 @@
struct SDL_Surface;
namespace bwidgets::widget
namespace bwidgets
{
class Caption : public Widget,
public FontHandler,
public TextureHandler
{
protected:
Font::RenderMode _render_mode {Font::RenderMode::SHADED};
std::string _text;
Texture* _text_texture {nullptr};
void _handle_font_change(Font*) override;
void _handle_font_color_change(const Color&, const Color&) override;
void _handle_geometry_change(const SDL_Rect&) noexcept override;
void _handle_renderer_change(Renderer*) override;
void _handle_rendering() override;
void _handle_texture_update() override;
public:
enum struct Alignment
{
@ -23,36 +35,20 @@ namespace bwidgets::widget
RIGHT
};
protected:
SDL_Color _color_fg {0, 0, 0, SDL_ALPHA_OPAQUE};
SDL_Color _color_bg {255, 255, 255, // NOLINT(readability-magic-numbers)
SDL_ALPHA_TRANSPARENT};
core::Font::RenderMode _render_mode {core::Font::RenderMode::SHADED};
std::string _text;
core::Texture* _text_texture {nullptr};
inline static const Color default_color_bg = {255, 255, 255, SDL_ALPHA_OPAQUE};
inline static const Color default_color_fg = {0, 0, 0, SDL_ALPHA_OPAQUE};
void _handle_font_change(core::Font* /*unused*/) override;
void _handle_font_color_change(const SDL_Color& /*unused*/,
const SDL_Color& /*unused*/) override;
void _handle_geometry_change(const SDL_Rect& /*unused*/) noexcept override;
void _handle_renderer_change(core::Renderer* /*unused*/) override;
void _handle_rendering() override;
void _handle_texture_update() override;
Alignment alignment {Alignment::LEFT};
Size margins {3, 3};
public:
Alignment alignment {Alignment::LEFT};
core::Size margins {3, 3};
Caption(Widget* parent = nullptr) : Widget(parent) {}
Caption(Widget* parent = nullptr);
~Caption() noexcept override;
virtual auto color_bg(const SDL_Color&) -> Caption*;
virtual auto color_fg(const SDL_Color&) -> Caption*;
virtual auto render_mode(core::Font::RenderMode) noexcept -> Caption*;
virtual auto render_mode(Font::RenderMode) noexcept -> Caption*;
[[nodiscard]] virtual auto text() const noexcept -> const std::string&;
virtual auto text(const std::string&) -> Caption*;
[[nodiscard]] auto size() const noexcept -> core::Size override;
[[nodiscard]] auto size() const noexcept -> Size override;
};
}

View File

@ -1,9 +1,9 @@
#ifndef BWIDGETS_FOCUS_HANDLER_HPP_
#define BWIDGETS_FOCUS_HANDLER_HPP_
#ifndef BWIDGETS_FOCUS_HANDLER_HPP
#define BWIDGETS_FOCUS_HANDLER_HPP
struct SDL_Rect;
namespace bwidgets::widget
namespace bwidgets
{
class FocusHandler
{

View File

@ -1,43 +1,57 @@
#ifndef BWIDGETS_FONT_HANDLER_HPP_
#define BWIDGETS_FONT_HANDLER_HPP_
#ifndef BWIDGETS_FONT_HANDLER_HPP
#define BWIDGETS_FONT_HANDLER_HPP
#include <basic_widgets/core/font.hpp>
namespace bwidgets::widget
namespace bwidgets
{
class FontHandler
{
protected:
core::Font* _font {nullptr};
SDL_Color _font_color_bg {255, 255, 255, // NOLINT(readability-magic-numbers)
SDL_ALPHA_OPAQUE};
SDL_Color _font_color_fg {0, 0, 0, SDL_ALPHA_OPAQUE};
core::Font::RenderMode _font_render_mode {core::Font::RenderMode::SHADED};
Font* _font {nullptr};
Color _font_color_bg = default_font_color_bg;
Color _font_color_fg = default_font_color_fg;
Font::RenderMode _font_render_mode {Font::RenderMode::SHADED};
virtual void _handle_font_change(core::Font*) = 0;
virtual void _handle_font_color_change(const SDL_Color&, const SDL_Color&) = 0;
virtual void _handle_font_change(Font*) = 0;
virtual void _handle_font_color_change(const Color&, const Color&) = 0;
public:
inline static const Color default_font_color_bg {255, 255, 255,
SDL_ALPHA_OPAQUE};
inline static const Color default_font_color_fg {0, 0, 0, SDL_ALPHA_OPAQUE};
virtual ~FontHandler() = default;
virtual inline auto font(core::Font* f) -> FontHandler* final
virtual inline auto font(Font* f) -> FontHandler* final
{
_handle_font_change(f);
_font = f;
if (f != nullptr
&& (f != _font || f->family_name != _font->family_name
|| f->height != _font->height || f->hinting() != _font->hinting()
|| f->kerning() != _font->kerning()
|| f->outline() != _font->outline()
|| f->style_name != _font->style_name))
{
_handle_font_change(f);
_font = f;
}
return this;
}
virtual inline auto font_color_bg(const SDL_Color& c) -> FontHandler* final
virtual inline auto font_color_bg(const Color& c) -> FontHandler* final
{
_handle_font_color_change(_font_color_bg, c);
_font_color_bg = c;
if (c != _font_color_bg) {
_handle_font_color_change(_font_color_bg, c);
_font_color_bg = c;
}
return this;
}
virtual inline auto font_color_fg(const SDL_Color& c) -> FontHandler* final
virtual inline auto font_color_fg(const Color& c) -> FontHandler* final
{
_handle_font_color_change(c, _font_color_bg);
_font_color_fg = c;
if (c != _font_color_fg) {
_handle_font_color_change(c, _font_color_fg);
_font_color_fg = c;
}
return this;
}
};

View File

@ -1,12 +1,12 @@
#ifndef BWIDGETS_KEYBOARD_HANDLER_
#define BWIDGETS_KEYBOARD_HANDLER_
#ifndef BWIDGETS_KEYBOARD_HANDLER
#define BWIDGETS_KEYBOARD_HANDLER
#include <basic_widgets/w/feat/focus_handler.hpp>
struct SDL_KeyboardEvent;
struct SDL_TextInputEvent;
namespace bwidgets::widget
namespace bwidgets
{
class KeyboardHandler : public virtual FocusHandler
{
@ -15,14 +15,18 @@ namespace bwidgets::widget
virtual void _handle_text_input(const SDL_TextInputEvent&) = 0;
public:
virtual inline void handle_keyboard(const SDL_KeyboardEvent& ev) final
virtual inline auto handle_keyboard(const SDL_KeyboardEvent& ev)
-> KeyboardHandler* final
{
if (_has_focus) _handle_key(ev);
return this;
}
virtual inline void handle_keyboard(const SDL_TextInputEvent& ev) final
virtual inline auto handle_keyboard(const SDL_TextInputEvent& ev)
-> KeyboardHandler* final
{
if (_has_focus) _handle_text_input(ev);
return this;
}
};
}

View File

@ -1,5 +1,5 @@
#ifndef BWIDGETS_MOUSE_HANDLER_HPP_
#define BWIDGETS_MOUSE_HANDLER_HPP_
#ifndef BWIDGETS_MOUSE_HANDLER_HPP
#define BWIDGETS_MOUSE_HANDLER_HPP
#include <functional>
@ -8,7 +8,7 @@
struct SDL_MouseButtonEvent;
struct SDL_MouseMotionEvent;
namespace bwidgets::widget
namespace bwidgets
{
class MouseHandler : public virtual FocusHandler
{

View File

@ -1,12 +1,12 @@
#ifndef BWIDGETS_TEXTURE_HANDLER_
#define BWIDGETS_TEXTURE_HANDLER_
#ifndef BWIDGETS_TEXTURE_HANDLER
#define BWIDGETS_TEXTURE_HANDLER
namespace bwidgets::core
namespace bwidgets
{
struct Renderer;
class Renderer;
}
namespace bwidgets::widget
namespace bwidgets
{
class TextureHandler
{

View File

@ -1,18 +0,0 @@
#ifndef BWIDGETS_HORIZONTAL_LAYOUT_HPP_
#define BWIDGETS_HORIZONTAL_LAYOUT_HPP_
#include <basic_widgets/w/base/layout.hpp>
namespace bwidgets::widget
{
class HorizontalLayout final : public Layout
{
private:
void _update_layout(const SDL_Rect& /*unused*/) noexcept override;
public:
[[nodiscard]] auto size() const noexcept -> core::Size override;
};
}
#endif

View File

@ -1,5 +1,5 @@
#ifndef BWIDGETS_NUMERIC_INPUT_HPP_
#define BWIDGETS_NUMERIC_INPUT_HPP_
#ifndef BWIDGETS_NUMERIC_INPUT_HPP
#define BWIDGETS_NUMERIC_INPUT_HPP
#include <limits>
@ -10,12 +10,12 @@
#include <type_traits>
namespace bwidgets::widget
namespace bwidgets
{
template<core::Numeric T>
template<Numeric T>
class NumericInput : public Input<T>
{
private:
protected:
Button _increment_button;
SDL_Rect _increment_button_area {};
Button _decrement_button;
@ -23,14 +23,14 @@ namespace bwidgets::widget
std::pair<T, T> _value_range {std::numeric_limits<T>::lowest(),
std::numeric_limits<T>::max()};
void _handle_font_change(core::Font* f) override
void _handle_font_change(Font* f) override
{
_decrement_button.font(f);
_increment_button.font(f);
Input<T>::_handle_font_change(f);
}
void _handle_font_color_change(const SDL_Color& fg, const SDL_Color& bg) override
void _handle_font_color_change(const Color& fg, const Color& bg) override
{
Input<T>::_handle_font_color_change(fg, bg);
_decrement_button.font_color_bg(bg)->font_color_fg(fg);
@ -45,16 +45,16 @@ namespace bwidgets::widget
? _increment_button.size().w
: _decrement_button.size().w;
const int button_area_width = 2 * widest_button;
const int spacing = core::center_line(button_area_width, widest_button);
const int field_width = vp.w - 2 * button_area_width;
const int spacing = center_line(button_area_width, widest_button);
const int field_width = vp.w - 2 * button_area_width;
Widget::_widget_area.x = Widget::_widget_area.x + button_area_width;
Widget::_widget_area.w = field_width;
Input<T>::_input_caption.viewport(core::rect_offset(
core::rect_margin(Widget::_widget_area,
{Input<T>::_border_width, Input<T>::_border_width}),
vp));
Input<T>::_input_caption.viewport(
rect_offset(rect_margin(Widget::_widget_area,
{Input<T>::border_width, Input<T>::border_width}),
vp));
_decrement_button_area = {spacing, Widget::_widget_area.y,
_decrement_button.size().w,
_decrement_button.size().h};
@ -63,11 +63,11 @@ namespace bwidgets::widget
Widget::_widget_area.y, _increment_button.size().w,
_increment_button.size().h};
_increment_button.viewport(core::rect_offset(_increment_button_area, vp));
_decrement_button.viewport(core::rect_offset(_decrement_button_area, vp));
_increment_button.viewport(rect_offset(_increment_button_area, vp));
_decrement_button.viewport(rect_offset(_decrement_button_area, vp));
}
void _handle_renderer_change(core::Renderer* r) override
void _handle_renderer_change(Renderer* r) override
{
Input<T>::_handle_renderer_change(r);
_decrement_button.renderer(r);
@ -87,7 +87,7 @@ namespace bwidgets::widget
NumericInput(Widget* parent = nullptr)
: Input<T>(parent), _increment_button(this), _decrement_button(this)
{
Input<T>::_input_caption.alignment = widget::Caption::Alignment::RIGHT;
Input<T>::_input_caption.alignment = Caption::Alignment::RIGHT;
Input<T>::input_min_width = 10; // NOLINT(readability-magic-numbers)
Input<T>::input_width_unit = '0';
@ -117,7 +117,7 @@ namespace bwidgets::widget
auto handle_event(const SDL_Event& ev) -> Widget* override
{
Widget::handle_event(ev);
Input<T>::handle_event(ev);
_increment_button.handle_event(ev);
_decrement_button.handle_event(ev);
return this;
@ -128,19 +128,19 @@ namespace bwidgets::widget
{
bool valid = false;
if (input.at(0) >= '0' && input.at(0) <= '9') valid = true;
if (input[0] >= '0' && input[0] <= '9') valid = true;
if constexpr (std::is_floating_point_v<T>)
if (input.at(0) == '.'
if (input[0] == '.'
&& Input<T>::input_text().find('.') == std::string::npos)
valid = true;
if constexpr (std::is_signed_v<T>) {
std::string displayed_value = Input<T>::input_text();
if (input.at(0) == '-' && displayed_value.empty()) valid = true;
if (input[0] == '-' && displayed_value.empty()) valid = true;
}
return valid;
}
auto process_value(T x) const noexcept -> T override
[[nodiscard]] auto process_value(T x) const noexcept -> T override
{
T value = x;
if (x < _value_range.first) value = _value_range.first;
@ -149,14 +149,14 @@ namespace bwidgets::widget
return value;
}
[[nodiscard]] inline auto size() const noexcept -> core::Size override
[[nodiscard]] inline auto size() const noexcept -> Size override
{
auto base = Input<T>::size();
auto btns_width = 4 * _increment_button.size().w;
return {base.w + btns_width, base.h};
}
virtual auto value_range() const noexcept -> std::pair<T, T>
[[nodiscard]] virtual auto value_range() const noexcept -> const std::pair<T, T>&
{
return _value_range;
}

View File

@ -1,18 +0,0 @@
#ifndef BWIDGETS_VERTICAL_LAYOUT_HPP_
#define BWIDGETS_VERTICAL_LAYOUT_HPP_
#include <basic_widgets/w/base/layout.hpp>
namespace bwidgets::widget
{
class VerticalLayout final : public Layout
{
private:
void _update_layout(const SDL_Rect& /*unused*/) noexcept override;
public:
[[nodiscard]] auto size() const noexcept -> core::Size override;
};
}
#endif

View File

@ -1,13 +1,16 @@
project('sdl2_basic_widgets', 'cpp',
version : '0.1pre',
default_options : [
'warning_level=3',
'b_lundef=false',
'b_sanitize=address,undefined',
'cpp_std=c++20',
'd_ndebug=if-release',
'optimization=g'],
'optimization=g',
'warning_level=3',
],
license: 'EUPL-1.2')
add_project_arguments('-pedantic', language: 'cpp')
add_project_arguments('-pedantic', '-Winline', language: 'cpp')
sdl = [
dependency('sdl2', version: '>=2.0.5'),
@ -22,15 +25,14 @@ libbasic_widgets = static_library('basic_widgets',
'src/core/font.cpp',
'src/core/renderer.cpp',
'src/core/texture.cpp',
'src/w/aligned_layout.cpp',
'src/w/base/layout.cpp',
'src/w/base/widget.cpp',
'src/w/button.cpp',
'src/w/caption.cpp',
'src/w/feat/font_handler.cpp',
'src/w/feat/keyboard_handler.cpp',
'src/w/feat/mouse_handler.cpp',
'src/w/button.cpp',
'src/w/caption.cpp',
'src/w/horizontal_layout.cpp',
'src/w/vertical_layout.cpp',
dependencies : [sdl, fontconfig],
include_directories : pub_api,
install : true)

View File

@ -8,28 +8,34 @@
#include <basic_widgets/core/math.hpp>
#include <basic_widgets/core/texture.hpp>
namespace bwidgets::core
namespace bwidgets
{
auto aa(const SDL_Color& base_color, const int aa_pixels, const float d) noexcept
-> SDL_Color
auto aa(const Color& base_color, const int aa_pixels, const float d) noexcept
-> Color
{
SDL_Color c {base_color};
Color c(base_color);
if (aa_pixels == 0) {
if (d > 0) c.a = 0;
if (d > 0) c().a = 0;
}
else {
const auto factor = 1.0 - smoothstep<float>(d, (float)-aa_pixels, 0.);
c.a = (uint8_t)((float)c.a * factor);
c().a = (uint8_t)((float)c().a * factor);
}
return c;
}
auto filled_circle(const SDL_Color& c, const int resolution, core::Renderer* r,
const int aa_pixels) -> core::Texture*
auto filled_circle(const Color& c, const int resolution, Renderer* r,
const int aa_pixels) -> Texture*
{
auto* texture {new core::Texture(
r, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, resolution, resolution)};
// clang-format off
auto* texture {new Texture(
r,
SDL_PIXELFORMAT_RGBA32,
SDL_TEXTUREACCESS_STATIC,
resolution,
resolution)};
// clang-format on
const auto radius {resolution / 2};
const SDL_Point center {radius, radius};
@ -37,10 +43,11 @@ namespace bwidgets::core
texture,
[aa_pixels, c, center, radius](const SDL_Point& p,
const SDL_PixelFormat* format) -> uint32_t {
const auto d_delta = core::distance(center, p) - (float)radius;
const auto d_delta = distance(center, p) - (float)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);
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);
@ -49,8 +56,9 @@ namespace bwidgets::core
}
void set_pixels_color(
core::Texture* t,
const std::function<Uint32(const SDL_Point&, const SDL_PixelFormat*)>& pixel_color)
Texture* t,
const std::function<uint32_t(const SDL_Point&, const SDL_PixelFormat*)>&
pixel_color)
{
auto attr = t->attributes();
auto pitch = attr.w * attr.format->BytesPerPixel;
@ -59,7 +67,7 @@ namespace bwidgets::core
pixels.reserve(attr.h * (std::vector<uint32_t>::size_type)pitch);
for (auto y = 0; y < attr.h; y++) {
for (auto x = 0; x < attr.w; x++) {
pixels.push_back(pixel_color({x, y}, attr.format));
pixels.emplace_back(pixel_color({x, y}, attr.format));
}
}

View File

@ -1,6 +1,5 @@
#include <any>
#include <functional>
#include <vector>
#include <stack>
#include <fontconfig/fontconfig.h>
@ -8,7 +7,7 @@
#include <basic_widgets/core/type/fc_error.hpp>
#include <basic_widgets/core/type/sdl_error.hpp>
namespace bwidgets::core
namespace bwidgets
{
Font::Font(TTF_Font* f)
: OpaqueStruct(f),
@ -29,44 +28,44 @@ namespace bwidgets::core
Font::~Font()
{
TTF_CloseFont(c_pod);
TTF_CloseFont(_c_pod);
}
auto Font::hinting() -> Font::Hinting
{
return (Hinting)TTF_GetFontHinting(c_pod);
return (Hinting)TTF_GetFontHinting(_c_pod);
}
auto Font::hinting(Font::Hinting h) -> Font*
{
TTF_SetFontHinting(c_pod, (int)h);
TTF_SetFontHinting(_c_pod, (int)h);
return this;
}
auto Font::kerning() -> bool
{
return TTF_GetFontHinting(c_pod) != 0;
return TTF_GetFontKerning(_c_pod) != 0;
}
auto Font::kerning(bool allowed) -> Font*
{
TTF_SetFontKerning(c_pod, static_cast<int>(allowed));
TTF_SetFontKerning(_c_pod, static_cast<int>(allowed));
return this;
}
auto Font::outline() -> int
{
return TTF_GetFontOutline(c_pod);
return TTF_GetFontOutline(_c_pod);
}
auto Font::outline(int size) -> Font*
{
TTF_SetFontOutline(c_pod, size);
TTF_SetFontOutline(_c_pod, size);
return this;
}
auto Font::render(RenderMode m, const std::string& str, const SDL_Color& fg,
const SDL_Color& bg) -> SDL_Surface*
auto Font::render(RenderMode m, const std::string& str, const Color& fg,
const Color& bg) -> SDL_Surface*
{
std::function<SDL_Surface*()> renderer;
const char* c_str = str.empty() ? " " : str.c_str();
@ -74,17 +73,17 @@ namespace bwidgets::core
switch (m) {
case RenderMode::BLENDED:
renderer = [&fg, c_str, this]() {
return TTF_RenderUTF8_Blended(c_pod, c_str, fg);
return TTF_RenderUTF8_Blended(_c_pod, c_str, fg());
};
break;
case RenderMode::SHADED:
renderer = [&bg, &fg, c_str, this]() {
return TTF_RenderUTF8_Shaded(c_pod, c_str, fg, bg);
return TTF_RenderUTF8_Shaded(_c_pod, c_str, fg(), bg());
};
break;
case RenderMode::SOLID:
renderer = [&fg, c_str, this]() {
return TTF_RenderUTF8_Solid(c_pod, c_str, fg);
return TTF_RenderUTF8_Solid(_c_pod, c_str, fg());
};
break;
}
@ -94,53 +93,45 @@ namespace bwidgets::core
auto Font::style() -> uint8_t
{
return TTF_GetFontStyle(c_pod);
return TTF_GetFontStyle(_c_pod);
}
auto Font::style(uint8_t s) -> Font*
{
TTF_SetFontStyle(c_pod, s);
TTF_SetFontStyle(_c_pod, s);
return this;
}
auto Font::text_size(const std::string& str) -> Size
{
Size s {};
TTF_SizeUTF8(c_pod, str.c_str(), &s.w, &s.h);
TTF_SizeUTF8(_c_pod, str.c_str(), &s.w, &s.h);
return s;
}
auto Font::find(const std::string& pat) -> std::string
{
std::vector<std::any> ptrs;
auto clean = [&ptrs](int lvl) mutable {
switch (lvl) {
case 2:
FcPatternDestroy(std::any_cast<FcPattern*>(ptrs.at(1)));
[[fallthrough]];
case 1:
FcConfigDestroy(std::any_cast<FcConfig*>(ptrs.at(0)));
FcFini();
}
};
std::stack<std::function<void()>> cleaners;
FcConfig* conf = ptr_or_throw<FCError>(FcInitLoadConfigAndFonts(), __FILE__,
__FUNCTION__, __LINE__, "init failed");
ptrs.emplace_back(conf);
cleaners.emplace([conf]() { FcConfigDestroy(conf); });
FcPattern* pattern = nullptr;
try {
pattern =
ptr_or_throw<FCError>(FcNameParse((FcChar8*)pat.c_str()), __FILE__,
__FUNCTION__, __LINE__, "pattern parsing failed");
ptrs.emplace_back(pattern);
cleaners.emplace([pattern]() { FcPatternDestroy(pattern); });
if (FcConfigSubstitute(conf, pattern, FcMatchPattern) == FcFalse)
throw FCError {__FILE__, __FUNCTION__, __LINE__,
"FcConfigSubstitute failed"};
} catch (const std::exception& e) {
clean((int)ptrs.size());
while (!cleaners.empty()) {
cleaners.top()();
cleaners.pop();
}
throw e;
}
@ -157,7 +148,10 @@ namespace bwidgets::core
FcPatternDestroy(font);
}
clean((int)ptrs.size());
while (!cleaners.empty()) {
cleaners.top()();
cleaners.pop();
}
if (file_path.empty())
throw FCError {__FILE__, __FUNCTION__, __LINE__, "no font found"};

View File

@ -3,101 +3,103 @@
using namespace bwidgets;
core::Renderer::Renderer(SDL_Renderer* r) : OpaqueStruct(r), info(_info(r)) {}
Renderer::Renderer(SDL_Renderer* r) : OpaqueStruct(r), info(_info(r)) {}
core::Renderer::Renderer(SDL_Window* w, int index, uint32_t flags = 0)
Renderer::Renderer(SDL_Window* w, int index, uint32_t flags = 0)
: Renderer(ptr_or_throw<SDLError>(SDL_CreateRenderer(w, index, flags), __FILE__,
__FUNCTION__, __LINE__))
{}
core::Renderer::~Renderer()
Renderer::~Renderer()
{
SDL_DestroyRenderer(c_pod);
SDL_DestroyRenderer(_c_pod);
}
auto core::Renderer::blend_mode() -> SDL_BlendMode
auto Renderer::blend_mode() -> SDL_BlendMode
{
SDL_BlendMode mode {};
SDLError::success_or_throw(SDL_GetRenderDrawBlendMode(c_pod, &mode), __FILE__,
SDLError::success_or_throw(SDL_GetRenderDrawBlendMode(_c_pod, &mode), __FILE__,
__FUNCTION__, __LINE__);
return mode;
}
auto core::Renderer::blend_mode(SDL_BlendMode mode) -> core::Renderer*
auto Renderer::blend_mode(SDL_BlendMode mode) -> Renderer*
{
SDLError::success_or_throw(SDL_SetRenderDrawBlendMode(c_pod, mode), __FILE__,
SDLError::success_or_throw(SDL_SetRenderDrawBlendMode(_c_pod, mode), __FILE__,
__FUNCTION__, __LINE__);
return this;
}
auto core::Renderer::clear() -> core::Renderer*
auto Renderer::clear() -> Renderer*
{
SDLError::success_or_throw(SDL_RenderClear(c_pod), __FILE__, __FUNCTION__, __LINE__);
SDLError::success_or_throw(SDL_RenderClear(_c_pod), __FILE__, __FUNCTION__,
__LINE__);
return this;
}
auto core::Renderer::copy(core::Texture* t, const SDL_Rect* src, const SDL_Rect* dst)
-> core::Renderer*
auto Renderer::copy(Texture* t, const SDL_Rect* src, const SDL_Rect* dst) -> Renderer*
{
SDLError::success_or_throw(SDL_RenderCopy(c_pod, t->c_pod, src, dst), __FILE__,
SDLError::success_or_throw(SDL_RenderCopy(_c_pod, (*t)(), src, dst), __FILE__,
__FUNCTION__, __LINE__);
return this;
}
auto core::Renderer::draw_color() -> SDL_Color
auto Renderer::draw_color() -> Color
{
SDL_Color c;
SDLError::success_or_throw(SDL_GetRenderDrawColor(c_pod, &c.r, &c.g, &c.b, &c.a),
__FILE__, __FUNCTION__, __LINE__);
Color c;
SDLError::success_or_throw(
SDL_GetRenderDrawColor(_c_pod, &c().r, &c().g, &c().b, &c().a), __FILE__,
__FUNCTION__, __LINE__);
return c;
}
auto core::Renderer::draw_color(const SDL_Color& c) -> core::Renderer*
auto Renderer::draw_color(const Color& c) -> Renderer*
{
SDLError::success_or_throw(SDL_SetRenderDrawColor(c_pod, c.r, c.g, c.b, c.a),
__FILE__, __FUNCTION__, __LINE__);
SDLError::success_or_throw(
SDL_SetRenderDrawColor(_c_pod, c().r, c().g, c().b, c().a), __FILE__, __FUNCTION__,
__LINE__);
return this;
}
auto core::Renderer::draw_line(const SDL_Point& a, const SDL_Point& b) -> core::Renderer*
auto Renderer::draw_line(const SDL_Point& a, const SDL_Point& b) -> Renderer*
{
SDLError::success_or_throw(SDL_RenderDrawLine(c_pod, a.x, a.y, b.x, b.y), __FILE__,
SDLError::success_or_throw(SDL_RenderDrawLine(_c_pod, a.x, a.y, b.x, b.y), __FILE__,
__FUNCTION__, __LINE__);
return this;
}
auto core::Renderer::draw_lines(const std::vector<SDL_Point>& pts) -> core::Renderer*
auto Renderer::draw_lines(const std::vector<SDL_Point>& pts) -> Renderer*
{
SDLError::success_or_throw(SDL_RenderDrawLines(c_pod, pts.data(), (int)pts.size()),
SDLError::success_or_throw(SDL_RenderDrawLines(_c_pod, pts.data(), (int)pts.size()),
__FILE__, __FUNCTION__, __LINE__);
return this;
}
auto core::Renderer::draw_point(const SDL_Point& p) -> core::Renderer*
auto Renderer::draw_point(const SDL_Point& p) -> Renderer*
{
SDLError::success_or_throw(SDL_RenderDrawPoint(c_pod, p.x, p.y), __FILE__,
SDLError::success_or_throw(SDL_RenderDrawPoint(_c_pod, p.x, p.y), __FILE__,
__FUNCTION__, __LINE__);
return this;
}
auto core::Renderer::draw_points(const std::vector<SDL_Point>& pts) -> core::Renderer*
auto Renderer::draw_points(const std::vector<SDL_Point>& pts) -> Renderer*
{
SDLError::success_or_throw(SDL_RenderDrawPoints(c_pod, pts.data(), (int)pts.size()),
SDLError::success_or_throw(SDL_RenderDrawPoints(_c_pod, pts.data(), (int)pts.size()),
__FILE__, __FUNCTION__, __LINE__);
return this;
}
auto core::Renderer::draw_rect(const SDL_Rect* r) -> core::Renderer*
auto Renderer::draw_rect(const SDL_Rect* r) -> Renderer*
{
auto vp = viewport();
// Has glitch at top-left and bottom-right corner.
@ -105,10 +107,10 @@ auto core::Renderer::draw_rect(const SDL_Rect* r) -> core::Renderer*
// The second corner is missing a pixel.
if (r != nullptr)
viewport({vp.x + r->x, vp.y + r->y, r->w - 1, r->h - 1}); // crop extra pixel
SDLError::success_or_throw(SDL_RenderDrawRect(c_pod, nullptr), __FILE__,
SDLError::success_or_throw(SDL_RenderDrawRect(_c_pod, nullptr), __FILE__,
__FUNCTION__, __LINE__);
if (r != nullptr) draw_point({r->w - 1, r->h - 1}); // add missing pixel
// add missing pixel. works sometimes…
if (r != nullptr) draw_point({r->w - 1, r->h - 1});
else draw_point({vp.w - 1, vp.h - 1});
viewport(vp);
@ -116,55 +118,55 @@ auto core::Renderer::draw_rect(const SDL_Rect* r) -> core::Renderer*
return this;
}
auto core::Renderer::draw_rects(const std::vector<SDL_Rect>& rs) -> core::Renderer*
auto Renderer::draw_rects(const std::vector<SDL_Rect>& rs) -> Renderer*
{
SDLError::success_or_throw(SDL_RenderDrawRects(c_pod, rs.data(), (int)rs.size()),
SDLError::success_or_throw(SDL_RenderDrawRects(_c_pod, rs.data(), (int)rs.size()),
__FILE__, __FUNCTION__, __LINE__);
return this;
}
auto core::Renderer::fill_rect(const SDL_Rect* r) -> core::Renderer*
auto Renderer::fill_rect(const SDL_Rect* r) -> Renderer*
{
SDLError::success_or_throw(SDL_RenderFillRect(c_pod, r), __FILE__, __FUNCTION__,
SDLError::success_or_throw(SDL_RenderFillRect(_c_pod, r), __FILE__, __FUNCTION__,
__LINE__);
return this;
}
auto core::Renderer::fill_rects(const std::vector<SDL_Rect>& rs) -> core::Renderer*
auto Renderer::fill_rects(const std::vector<SDL_Rect>& rs) -> Renderer*
{
SDLError::success_or_throw(SDL_RenderFillRects(c_pod, rs.data(), (int)rs.size()),
SDLError::success_or_throw(SDL_RenderFillRects(_c_pod, rs.data(), (int)rs.size()),
__FILE__, __FUNCTION__, __LINE__);
return this;
}
auto core::Renderer::output_size() -> core::Size
auto Renderer::output_size() -> Size
{
Size s {};
SDLError::success_or_throw(SDL_GetRendererOutputSize(c_pod, &s.w, &s.h), __FILE__,
SDLError::success_or_throw(SDL_GetRendererOutputSize(_c_pod, &s.w, &s.h), __FILE__,
__FUNCTION__, __LINE__);
return s;
}
void core::Renderer::present()
void Renderer::present()
{
SDL_RenderPresent(c_pod);
SDL_RenderPresent(_c_pod);
}
auto core::Renderer::viewport() -> SDL_Rect
auto Renderer::viewport() -> SDL_Rect
{
SDL_Rect vp;
SDL_RenderGetViewport(c_pod, &vp);
SDL_RenderGetViewport(_c_pod, &vp);
return vp;
}
auto core::Renderer::viewport(const SDL_Rect* vp) -> core::Renderer*
auto Renderer::viewport(const SDL_Rect* vp) -> Renderer*
{
SDLError::success_or_throw(SDL_RenderSetViewport(c_pod, vp), __FILE__, __FUNCTION__,
SDLError::success_or_throw(SDL_RenderSetViewport(_c_pod, vp), __FILE__, __FUNCTION__,
__LINE__);
return this;

View File

@ -3,101 +3,101 @@
using namespace bwidgets;
core::Texture::Texture(SDL_Texture* t) : OpaqueStruct(t)
Texture::Texture(SDL_Texture* t) : OpaqueStruct(t)
{
_attributes = attributes(t);
}
core::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__,
Texture::Texture(Renderer* r, SDL_PixelFormatEnum f, SDL_TextureAccess a, int w, int h)
: Texture(ptr_or_throw<SDLError>(SDL_CreateTexture((*r)(), f, a, w, h), __FILE__,
__FUNCTION__, __LINE__))
{}
core::Texture::Texture(Renderer* r, SDL_Surface* s)
: OpaqueStruct(ptr_or_throw<SDLError>(SDL_CreateTextureFromSurface(r->c_pod, s),
Texture::Texture(Renderer* r, SDL_Surface* s)
: OpaqueStruct(ptr_or_throw<SDLError>(SDL_CreateTextureFromSurface((*r)(), s),
__FILE__, __FUNCTION__, __LINE__))
{
_attributes = attributes(c_pod);
_attributes = attributes(_c_pod);
}
core::Texture::~Texture() noexcept
Texture::~Texture() noexcept
{
SDL_DestroyTexture(c_pod);
c_pod = nullptr;
SDL_DestroyTexture(_c_pod);
_c_pod = nullptr;
}
auto core::Texture::alpha_mode() -> uint8_t
auto Texture::alpha_mode() -> uint8_t
{
uint8_t mode = 0;
SDLError::success_or_throw(SDL_GetTextureAlphaMod(c_pod, &mode), __FILE__,
SDLError::success_or_throw(SDL_GetTextureAlphaMod(_c_pod, &mode), __FILE__,
__FUNCTION__, __LINE__);
return mode;
}
auto core::Texture::alpha_mode(uint8_t m) -> core::Texture*
auto Texture::alpha_mode(uint8_t m) -> Texture*
{
SDLError::success_or_throw(SDL_SetTextureAlphaMod(c_pod, m), __FILE__, __FUNCTION__,
SDLError::success_or_throw(SDL_SetTextureAlphaMod(_c_pod, m), __FILE__, __FUNCTION__,
__LINE__);
return this;
}
auto core::Texture::blend_mode() -> SDL_BlendMode
auto Texture::blend_mode() -> SDL_BlendMode
{
SDL_BlendMode mode {};
SDLError::success_or_throw(SDL_GetTextureBlendMode(c_pod, &mode), __FILE__,
SDLError::success_or_throw(SDL_GetTextureBlendMode(_c_pod, &mode), __FILE__,
__FUNCTION__, __LINE__);
return mode;
}
auto core::Texture::blend_mode(SDL_BlendMode m) -> core::Texture*
auto Texture::blend_mode(SDL_BlendMode m) -> Texture*
{
SDLError::success_or_throw(SDL_SetTextureBlendMode(c_pod, m), __FILE__, __FUNCTION__,
__LINE__);
SDLError::success_or_throw(SDL_SetTextureBlendMode(_c_pod, m), __FILE__,
__FUNCTION__, __LINE__);
return this;
}
auto core::Texture::color_mode() -> SDL_Color
auto Texture::color_mode() -> Color
{
SDL_Color mode;
SDLError::success_or_throw(SDL_GetTextureColorMod(c_pod, &mode.r, &mode.g, &mode.b),
Color mode;
SDLError::success_or_throw(
SDL_GetTextureColorMod(_c_pod, &mode().r, &mode().g, &mode().b), __FILE__,
__FUNCTION__, __LINE__);
return mode;
}
auto Texture::color_mode(const Color& m) -> Texture*
{
SDLError::success_or_throw(SDL_SetTextureColorMod(_c_pod, m().r, m().g, m().b),
__FILE__, __FUNCTION__, __LINE__);
return mode;
}
auto core::Texture::color_mode(const SDL_Color& m) -> core::Texture*
{
SDLError::success_or_throw(SDL_SetTextureColorMod(c_pod, m.r, m.g, m.b), __FILE__,
__FUNCTION__, __LINE__);
return this;
}
auto core::Texture::scale_mode() -> SDL_ScaleMode
auto Texture::scale_mode() -> SDL_ScaleMode
{
SDL_ScaleMode mode {};
SDLError::success_or_throw(SDL_GetTextureScaleMode(c_pod, &mode), __FILE__,
SDLError::success_or_throw(SDL_GetTextureScaleMode(_c_pod, &mode), __FILE__,
__FUNCTION__, __LINE__);
return mode;
}
auto core::Texture::scale_mode(SDL_ScaleMode m) -> core::Texture*
auto Texture::scale_mode(SDL_ScaleMode m) -> Texture*
{
SDLError::success_or_throw(SDL_SetTextureScaleMode(c_pod, m), __FILE__, __FUNCTION__,
__LINE__);
return this;
}
auto core::Texture::update(SDL_Rect* r, const void* pixels, int pitch) -> core::Texture*
{
SDLError::success_or_throw(SDL_UpdateTexture(c_pod, r, pixels, pitch), __FILE__,
SDLError::success_or_throw(SDL_SetTextureScaleMode(_c_pod, m), __FILE__,
__FUNCTION__, __LINE__);
return this;
}
auto Texture::update(SDL_Rect* r, const void* pixels, int pitch) -> Texture*
{
SDLError::success_or_throw(SDL_UpdateTexture(_c_pod, r, pixels, pitch), __FILE__,
__FUNCTION__, __LINE__);
return this;

57
src/w/aligned_layout.cpp Normal file
View File

@ -0,0 +1,57 @@
#include <basic_widgets/core/math.hpp>
#include <basic_widgets/w/aligned_layout.hpp>
using namespace bwidgets;
auto AlignedLayout::size() const noexcept -> Size
{
Size min_size {0, 0};
if (alignment == Alignment::HORIZONTAL) {
for (const auto* w : _widgets) {
if (w->size().w > min_size.w) min_size.w = w->size().w;
if (w->size().h > min_size.h) min_size.h = w->size().h;
}
return {(int)_widgets.size() * min_size.w
+ ((int)_widgets.size() + 1) * margins.w,
min_size.h + 2 * margins.h};
}
for (const auto* w : _widgets) {
if (w->size().w > min_size.w) min_size.w = w->size().w;
min_size.h += w->size().h;
}
return {min_size.w + 2 * margins.w,
min_size.h + ((int)_widgets.size() + 1) * margins.h};
}
void AlignedLayout::_update_layout(const SDL_Rect& vp) noexcept
{
if (alignment == Alignment::HORIZONTAL) {
int widget_width =
(vp.w - ((int)_widgets.size() + 1) * margins.w) / (int)_widgets.size();
for (std::vector<Widget*>::size_type i = 0; i < _widgets.size(); i++) {
auto* w = _widgets[i];
w->viewport({vp.x + margins.w + (int)i * (widget_width + margins.w),
vp.y + center_line(vp.h, w->size().h), widget_width,
w->size().h});
}
}
else {
int offset = 0;
for (std::vector<Widget*>::size_type i = 0; i < _widgets.size(); i++) {
auto* w = _widgets[i];
w->viewport({
vp.x,
vp.y + ((int)i + 1) * margins.h + offset,
vp.w,
w->size().h,
});
offset += w->size().h;
}
}
}

View File

@ -2,40 +2,41 @@
using namespace bwidgets;
widget::Layout::~Layout() noexcept
Layout::~Layout() noexcept
{
for (Widget* widget_ptr : _widgets) delete widget_ptr;
for (auto* widget_ptr : _widgets) delete widget_ptr;
}
auto widget::Layout::add_widget(Widget* widget_ptr) -> widget::Layout*
auto Layout::add_widget(Widget* widget_ptr) -> Layout*
{
widget_ptr->renderer(_renderer);
_widgets.push_back(widget_ptr);
_widgets.emplace_back(widget_ptr);
_update_layout(_viewport);
return this;
}
void widget::Layout::for_widgets(const std::function<void(Widget*)>& f)
void Layout::for_widgets(const std::function<void(Widget*)>& f)
{
for (auto* w : _widgets) f(w);
}
auto widget::Layout::handle_event(const SDL_Event& ev) -> widget::Layout*
auto Layout::handle_event(const SDL_Event& ev) -> Layout*
{
for (Widget* widget_ptr : _widgets) widget_ptr->handle_event(ev);
for (auto* widget_ptr : _widgets) widget_ptr->handle_event(ev);
return this;
}
void widget::Layout::_handle_geometry_change(const SDL_Rect& vp) noexcept
void Layout::_handle_geometry_change(const SDL_Rect& vp) noexcept
{
_update_layout(vp);
}
void widget::Layout::_handle_renderer_change(core::Renderer* r)
void Layout::_handle_renderer_change(Renderer* r)
{
for (auto* w : _widgets) w->renderer(r);
}
void widget::Layout::_handle_rendering()
void Layout::_handle_rendering()
{
for (Widget* widget_ptr : _widgets) widget_ptr->render();
for (auto* widget_ptr : _widgets) widget_ptr->render();
}

View File

@ -7,9 +7,9 @@
using namespace bwidgets;
auto widget::Widget::handle_event(const SDL_Event& ev) -> widget::Widget*
auto Widget::handle_event(const SDL_Event& ev) -> Widget*
{
if (auto* handler = dynamic_cast<KeyboardHandler*>(this); handler != nullptr) {
if (auto* handler = dynamic_cast<KeyboardHandler*>(this); handler) {
switch (ev.type) {
case SDL_KEYDOWN:
case SDL_KEYUP:
@ -21,7 +21,7 @@ auto widget::Widget::handle_event(const SDL_Event& ev) -> widget::Widget*
}
}
if (auto* handler = dynamic_cast<MouseHandler*>(this); handler != nullptr) {
if (auto* handler = dynamic_cast<MouseHandler*>(this); handler) {
switch (ev.type) {
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
@ -36,13 +36,18 @@ auto widget::Widget::handle_event(const SDL_Event& ev) -> widget::Widget*
return this;
}
auto widget::Widget::render() -> widget::Widget*
auto Widget::render() -> Widget*
{
SDL_assert_release(_renderer); // NOLINT
if (_renderer == nullptr) return this;
#ifdef _NDEBUG
_renderer->draw_color({0, 255, 0, SDL_ALPHA_TRANSPARENT})->draw_rect(nullptr);
#ifndef _NDEBUG
_renderer
->draw_color({
// NOLINTNEXTLINE(readability-magic-numbers)
{0, 255, 0, SDL_ALPHA_TRANSPARENT}
})
->draw_rect(nullptr);
#endif
_renderer->viewport(_viewport);
@ -50,3 +55,24 @@ auto widget::Widget::render() -> widget::Widget*
return this;
}
auto Widget::renderer(Renderer* r) -> Widget*
{
if (r != _renderer) {
_handle_renderer_change(r);
_renderer = r;
}
return this;
}
auto Widget::viewport(const SDL_Rect& vp) noexcept -> Widget*
{
_handle_geometry_change(vp);
_viewport = vp;
return this;
}
auto Widget::viewport() const noexcept -> const SDL_Rect&
{
return _viewport;
}

View File

@ -1,3 +1,5 @@
#include <cmath>
#include <SDL2/SDL_render.h>
#include <basic_widgets/core/math.hpp>
@ -5,105 +7,96 @@
using namespace bwidgets;
widget::Button::Button(Widget* parent) : Widget(parent), _caption(this)
Button::Button(Widget* parent) : Widget(parent), _caption(this)
{
_focus_area = _click_area = &_widget_area;
_caption.alignment = Caption::Alignment::CENTER;
_caption.render_mode(core::Font::RenderMode::BLENDED);
_caption.render_mode(Font::RenderMode::BLENDED);
border_gradient = [this](int len, int pos) -> SDL_Color {
const auto& end_color = _is_hovered ? color_bg_hover : color_bg;
const SDL_Color start_color {(uint8_t)(end_color.r / 1.4),
(uint8_t)(end_color.g / 1.4),
(uint8_t)(end_color.b / 1.4), end_color.a};
const auto factor = core::linear(pos, 0, len);
return {(uint8_t)(std::lerp(start_color.r, end_color.r, factor)),
(uint8_t)(std::lerp(start_color.g, end_color.g, factor)),
(uint8_t)(std::lerp(start_color.b, end_color.b, factor)), end_color.a};
border_gradient = [this](int len, int pos) -> Color {
const auto& end_color = _is_hovered ? color_bg_hover : color_bg;
const auto start_color = end_color / 2;
const auto factor = linear(pos, 0, len);
return lerp(start_color, end_color, factor);
};
border_gradient_pushed = [this](int len, int pos) -> SDL_Color {
const auto& end_color = _is_hovered ? color_bg_hover : color_bg;
const SDL_Color start_color {(uint8_t)(end_color.r / 1.8),
(uint8_t)(end_color.g / 1.8),
(uint8_t)(end_color.b / 1.8), end_color.a};
const auto factor = core::linear(pos, 0, len);
return {(uint8_t)(std::lerp(start_color.r, end_color.r, factor)),
(uint8_t)(std::lerp(start_color.g, end_color.g, factor)),
(uint8_t)(std::lerp(start_color.b, end_color.b, factor)), end_color.a};
border_gradient_pushed = [this](int len, int pos) -> Color {
const auto& end_color = _is_hovered ? color_bg_hover : color_bg;
const Color start_color = end_color / 1.5;
const auto factor = linear(pos, 0, len);
return lerp(start_color, end_color, factor);
};
}
auto widget::Button::size() const noexcept -> core::Size
auto Button::size() const noexcept -> Size
{
return {_caption.size().w + 2 * border_size.w,
_caption.size().h + 2 * border_size.h};
return _caption.size() + border_size * 2;
}
auto widget::Button::text() const noexcept -> const std::string&
auto Button::text() const noexcept -> const std::string&
{
return _caption.text();
}
auto widget::Button::text(const std::string& txt) -> widget::Button*
auto Button::text(const std::string& txt) -> Button*
{
_caption.text(txt);
_handle_geometry_change(_viewport);
return this;
}
void widget::Button::_handle_font_change(core::Font* f)
void Button::_handle_font_change(Font* f)
{
_caption.font(f);
_handle_geometry_change(_viewport);
}
void widget::Button::_handle_font_color_change(const SDL_Color& fg, const SDL_Color&)
void Button::_handle_font_color_change(const Color& fg, const Color&)
{
_caption.color_fg(fg);
_caption.font_color_fg(fg);
}
void widget::Button::_handle_geometry_change(const SDL_Rect& vp) noexcept
void Button::_handle_geometry_change(const SDL_Rect& vp) noexcept
{
int h = _caption.size().h + 2 * border_size.h;
_widget_area = {0, core::center_line(vp.h, _caption.size().h) - border_size.h, vp.w,
h};
_widget_area = {0, center_line(vp.h, _caption.size().h) - border_size.h, vp.w, h};
auto txt_size = _caption.size();
_caption_area = {core::center_line(vp.w, txt_size.w),
core::center_line(vp.h, txt_size.h), txt_size.w, txt_size.h};
_caption.viewport(core::rect_offset(_caption_area, vp));
_caption_area = {center_line(vp.w, txt_size.w), center_line(vp.h, txt_size.h),
txt_size.w, txt_size.h};
_caption.viewport(rect_offset(_caption_area, vp));
}
void widget::Button::_handle_renderer_change(core::Renderer* r)
void Button::_handle_renderer_change(Renderer* r)
{
_caption.renderer(r);
}
void widget::Button::_handle_rendering()
void Button::_handle_rendering()
{
const SDL_Color& c = _is_hovered ? color_bg_hover : color_bg;
const auto& gradient = _is_pushed ? border_gradient_pushed : border_gradient;
auto x = 0;
auto y = 0;
const auto biggest = border_size.w > border_size.h ? border_size.w : border_size.h;
const Color& c = _is_hovered ? color_bg_hover : color_bg;
const auto& gradient = _is_pushed ? border_gradient_pushed : border_gradient;
auto x = 0;
auto y = 0;
const auto biggest = border_size.w > border_size.h ? border_size.w : border_size.h;
while (x < border_size.w || y < border_size.h) {
const auto max = x > y ? x : y;
const auto margin = core::Size({x, y});
_renderer->draw_color(gradient(biggest, max))
->draw_rect(core::rect_margin(_widget_area, margin));
const auto margin = Size({x, y});
_renderer->draw_color(gradient(biggest, max)())
->draw_rect(rect_margin(_widget_area, margin));
if (x < border_size.w) x++;
if (y < border_size.h) y++;
}
_renderer->draw_color(c)->fill_rect(core::rect_margin(_widget_area, border_size));
_caption.color_bg(c)->render();
_renderer->draw_color(c())->fill_rect(rect_margin(_widget_area, border_size));
_caption.font_color_bg(c());
_caption.render();
}
void widget::Button::_on_push(bool state)
void Button::_on_push(bool state)
{
SDL_Point offset {_viewport.x, _viewport.y};
if (state) {
@ -111,5 +104,5 @@ void widget::Button::_on_push(bool state)
offset.y += 1;
}
_caption.viewport(core::rect_offset(_caption_area, offset));
_caption.viewport(rect_offset(_caption_area, offset));
}

View File

@ -3,106 +3,96 @@
#include <basic_widgets/core/math.hpp>
#include <basic_widgets/core/texture.hpp>
#include <basic_widgets/w/caption.hpp>
#include <basic_widgets/w/numeric_input.hpp>
using namespace bwidgets;
widget::Caption::~Caption() noexcept
Caption::Caption(Widget* parent) : Widget(parent)
{
core::discard(_text_texture);
_font_color_bg = default_font_color_bg;
_font_color_fg = default_font_color_fg;
}
auto widget::Caption::color_bg(const SDL_Color& c) -> widget::Caption*
Caption::~Caption() noexcept
{
_color_bg = c;
if (_render_mode == core::Font::RenderMode::SHADED)
core::discard(_text_texture);
discard(_text_texture);
}
auto Caption::render_mode(Font::RenderMode m) noexcept -> Caption*
{
if (m != _render_mode) {
discard(_text_texture);
_render_mode = m;
}
return this;
}
auto widget::Caption::color_fg(const SDL_Color& c) -> widget::Caption*
auto Caption::size() const noexcept -> Size
{
_color_fg = c;
core::discard(_text_texture);
return this;
}
if (_font == nullptr) return {0, 0};
auto widget::Caption::render_mode(core::Font::RenderMode m) noexcept -> widget::Caption*
{
_render_mode = m;
core::discard(_text_texture);
return this;
}
auto widget::Caption::size() const noexcept -> core::Size
{
if (_font == nullptr) return {-1, -1};
core::Size size = _font->text_size(_text);
Size size = _font->text_size(_text);
return {size.w + 2 * margins.w, size.h + 2 * margins.h};
}
auto widget::Caption::text() const noexcept -> const std::string&
auto Caption::text() const noexcept -> const std::string&
{
return _text;
}
auto widget::Caption::text(const std::string& t) -> widget::Caption*
auto Caption::text(const std::string& t) -> Caption*
{
_text = t;
core::discard(_text_texture);
if (t != _text) {
discard(_text_texture);
_text = t;
}
return this;
}
void widget::Caption::_handle_font_change(core::Font* /*unused*/)
void Caption::_handle_font_change(Font*)
{
core::discard(_text_texture);
discard(_text_texture);
}
static inline auto operator!=(const SDL_Color& a, const SDL_Color& b) -> bool
{
return a.r != b.r || a.g != b.g || a.b != b.b || a.a != b.a;
}
void widget::Caption::_handle_font_color_change(const SDL_Color& fg, const SDL_Color& bg)
void Caption::_handle_font_color_change(const Color& fg, const Color& bg)
{
if (fg != _font_color_fg
|| (bg != _font_color_bg && _font_render_mode == core::Font::RenderMode::SHADED))
|| (bg != _font_color_bg && _font_render_mode == Font::RenderMode::SHADED))
{
core::discard(_text_texture);
discard(_text_texture);
_font_color_bg = bg;
}
}
void widget::Caption::_handle_geometry_change(const SDL_Rect& vp) noexcept
void Caption::_handle_geometry_change(const SDL_Rect& vp) noexcept
{
if (vp.w != _viewport.w || vp.h != _viewport.h) {
_widget_area = core::rect_margin({0, 0, vp.w, vp.h}, margins);
_widget_area = rect_margin({0, 0, vp.w, vp.h}, margins);
}
}
void widget::Caption::_handle_renderer_change(core::Renderer* /*unused*/)
void Caption::_handle_renderer_change(Renderer*)
{
core::discard(_text_texture);
discard(_text_texture);
}
void widget::Caption::_handle_rendering()
void Caption::_handle_rendering()
{
if (_text_texture == nullptr) _handle_texture_update();
if (_render_mode == core::Font::RenderMode::SHADED)
_renderer->draw_color(_color_bg)->fill_rect({0, 0, _viewport.w, _viewport.h});
if (_render_mode == Font::RenderMode::SHADED) {
_renderer->draw_color(_font_color_bg)
->fill_rect({0, 0, _viewport.w, _viewport.h});
}
if (_text_texture == nullptr) _handle_texture_update();
core::Size size_dst {
(int)((float)_text_texture->attributes().w / (float)_text_texture->attributes().h
* _widget_area.h), // NOLINT(bugprone-narrowing-conversions)
_widget_area.h};
Size size_dst {(int)((float)_text_texture->attributes().w
/ (float)_text_texture->attributes().h * (float)_widget_area.h),
_widget_area.h};
SDL_Rect texture_dst {margins.w, margins.h, size_dst.w, size_dst.h};
switch (alignment) {
case Alignment::CENTER:
texture_dst.x =
core::center_line(_widget_area.w, texture_dst.w) + _widget_area.x;
texture_dst.x = center_line(_widget_area.w, texture_dst.w) + _widget_area.x;
break;
case Alignment::LEFT:
break;
@ -113,19 +103,18 @@ void widget::Caption::_handle_rendering()
_renderer->copy(_text_texture, nullptr, texture_dst);
}
void widget::Caption::_handle_texture_update()
void Caption::_handle_texture_update()
{
SDL_assert_release(_font); // NOLINT
core::discard(_text_texture);
SDL_Surface* s = nullptr;
SDL_assert_release(_font && _text_texture == nullptr); // NOLINT
SDL_Surface* s;
switch (_render_mode) {
case core::Font::RenderMode::SHADED:
s = _font->render(_render_mode, _text, _color_fg, _color_bg);
case Font::RenderMode::SHADED:
s = _font->render(_render_mode, _text, _font_color_fg, _font_color_bg);
break;
default:
s = _font->render(_render_mode, _text, _color_fg);
s = _font->render(_render_mode, _text, _font_color_fg);
break;
}
_text_texture = new core::Texture(_renderer, s);
_text_texture = new Texture(_renderer, s);
SDL_FreeSurface(s);
}

View File

@ -6,13 +6,13 @@
using namespace bwidgets;
auto widget::MouseHandler::handle_mouse(const SDL_MouseButtonEvent& ev,
const SDL_Rect& orig) -> MouseHandler*
auto MouseHandler::handle_mouse(const SDL_MouseButtonEvent& ev, const SDL_Rect& orig)
-> MouseHandler*
{
if (_click_area == nullptr) return this;
SDL_Point p {ev.x, ev.y};
SDL_Rect vp {core::rect_offset(*_click_area, orig)};
SDL_Rect vp {rect_offset(*_click_area, orig)};
if (ev.type == SDL_MOUSEBUTTONDOWN) {
if (SDL_PointInRect(&p, &vp) == SDL_TRUE) {
@ -36,13 +36,13 @@ auto widget::MouseHandler::handle_mouse(const SDL_MouseButtonEvent& ev,
return this;
}
auto widget::MouseHandler::handle_mouse(const SDL_MouseMotionEvent& ev,
const SDL_Rect& orig) -> MouseHandler*
auto MouseHandler::handle_mouse(const SDL_MouseMotionEvent& ev, const SDL_Rect& orig)
-> MouseHandler*
{
if (_click_area == nullptr) return this;
SDL_Point p {ev.x, ev.y};
SDL_Rect vp {core::rect_offset(*_click_area, orig)};
SDL_Rect vp {rect_offset(*_click_area, orig)};
_is_hovered = SDL_PointInRect(&p, &vp) != 0;
@ -51,7 +51,7 @@ auto widget::MouseHandler::handle_mouse(const SDL_MouseMotionEvent& ev,
return this;
}
auto widget::MouseHandler::push(bool state) -> MouseHandler*
auto MouseHandler::push(bool state) -> MouseHandler*
{
_on_push(state);
_is_pushed = state;

View File

@ -1,30 +0,0 @@
#include <basic_widgets/core/math.hpp>
#include <basic_widgets/w/horizontal_layout.hpp>
using namespace bwidgets;
auto widget::HorizontalLayout::size() const noexcept -> core::Size
{
core::Size max {0, 0};
for (const auto* w : _widgets) {
if (w->size().w > max.w) max.w = w->size().w;
if (w->size().h > max.h) max.h = w->size().h;
}
return {(int)_widgets.size() * max.w + ((int)_widgets.size() + 1) * margins.w,
max.h + 2 * margins.h};
}
void widget::HorizontalLayout::_update_layout(const SDL_Rect& vp) noexcept
{
int widget_size =
(int)((vp.w - (_widgets.size() + 1) * margins.w) / _widgets.size());
for (std::vector<Widget*>::size_type i = 0; i < _widgets.size(); i++) {
auto* w = _widgets[i];
w->viewport({vp.x + margins.w + (int)i * (widget_size + margins.w),
vp.y + core::center_line(vp.h, w->size().h), widget_size,
w->size().h});
}
}

View File

@ -1,32 +0,0 @@
#include <basic_widgets/core/math.hpp>
#include <basic_widgets/w/vertical_layout.hpp>
using namespace bwidgets;
auto widget::VerticalLayout::size() const noexcept -> core::Size
{
core::Size size {0, 0};
for (const auto* w : _widgets) {
if (w->size().w > size.w) size.w = w->size().w;
size.h += w->size().h;
}
return {size.w + 2 * margins.w, size.h + ((int)_widgets.size() + 1) * margins.h};
}
void widget::VerticalLayout::_update_layout(const SDL_Rect& vp) noexcept
{
int offset = 0;
for (std::vector<Widget*>::size_type i = 0; i < _widgets.size(); i++) {
auto* w = _widgets[i];
w->viewport({
vp.x,
vp.y + ((int)i + 1) * margins.h + offset,
vp.w,
w->size().h,
});
offset += w->size().h;
}
}