Comment a bit the code.

This commit is contained in:
Andrea Blankenstijn 2021-08-23 23:50:14 +02:00
parent 5020eab828
commit f6ae2c51e8
35 changed files with 154 additions and 18 deletions

View File

@ -13,9 +13,18 @@ namespace bwidgets
class Renderer;
class Texture;
[[nodiscard]] auto aa(Color, int, float) noexcept -> Color;
[[nodiscard]] auto filled_circle(Color, int resolution, const Renderer&,
// Add transparency to color c relative to the distance d from
// a limit to produce an AntiAliasing effect.
// d >= 0 → full transparency;
// (aa_pixels) <= d < 0 → smoothsteped transparency gradient;
// d < (aa_pixels) → fully opaque.
[[nodiscard]] auto aa(Color c, int aa_pixels, float d) noexcept -> Color;
// Render a filled circle texture of color c and diameter resolution with
// aa_pixels used for antialiasing.
[[nodiscard]] auto filled_circle(Color c, int resolution, const Renderer&,
int aa_pixels = 3) -> std::shared_ptr<Texture>;
// Set the color of every pixels of a Texture using the passed function to compute
// pixel color.
void
set_pixels_color(Texture*,
const std::function<uint32_t(SDL_Point, const SDL_PixelFormat&)>&);

View File

@ -11,6 +11,7 @@ extern "C" {
namespace bwidgets
{
// Throw a E exception with message w if ptr is null. Otherwise return ptr.
template<Exception E, typename T>
inline auto ptr_or_throw(T* const ptr, const std::string_view w = "unknown") -> T*
{
@ -23,11 +24,15 @@ namespace bwidgets
return ptr;
}
// Check a return code with the success function passed. If success return false
// throw an E Exception with message w.
template<Exception E, typename T>
inline auto success_or_throw(
const T code, const std::string_view w = "unknown",
const std::function<bool(T)>& success = [](T code) { return code == 0; }) -> T
{
// If exception type is SDLError and no value or unknown was passed for w, get
// the last error message using SDL facilities.
if constexpr (std::is_same_v<E, SDLError>) {
const std::string what {w == "unknown" ? SDL_GetError() : w};
if (code < 0) throw E(what);

View File

@ -11,6 +11,7 @@
namespace bwidgets
{
// Wrap TTF_Font from SDL_ttf and provide a font finding function.
class Font final : OpaqueStruct<TTF_Font>::Wrapper
{
public:
@ -71,6 +72,7 @@ namespace bwidgets
auto style(uint8_t) noexcept -> Font*;
[[nodiscard]] auto text_size(std::string_view) const noexcept -> Size;
// Get file path of the font best matching a give fontconfig pattern.
[[nodiscard]] static auto find(std::string_view) -> std::string;
};
}

View File

@ -11,11 +11,16 @@
namespace bwidgets
{
// Get the start coordinate offset required to center a ligne with a length used_len on another
// line with a length available_len.
[[nodiscard]] inline auto center_line(const int available_len, const int used_len) noexcept -> int
{
return (available_len - used_len) / 2;
}
// Get the squared distance of two points on a 2D plan. This avoid computing square root
// when for instance we need to compare two lenght but knowing the actual lenght is not
// needed.
[[nodiscard]] inline auto distance_sqrd(const SDL_Point a, const SDL_Point b) noexcept
-> float
{
@ -23,11 +28,13 @@ namespace bwidgets
+ float(a.y - b.y) * float(a.y - b.y);
}
// Get distance of two points on a 2D plan.
[[nodiscard]] inline auto distance(const SDL_Point a, const SDL_Point b) noexcept -> float
{
return std::sqrt(distance_sqrd(a, b));
}
// Like std::lerp but for use with Color object. lerp function is applied only to enabled channels.
template<FloatingPoint F>
[[nodiscard]] inline auto lerp(const Color a, const Color b, const F x, const bool op_alpha=false, const bool op_color=true) noexcept -> Color
{
@ -39,12 +46,14 @@ namespace bwidgets
}};
}
// Reverse lerp.
template<Numeric N>
[[nodiscard]] inline auto linear(const N x, const N a, const N b) noexcept -> float
{
return (float)(x - a) / (float)(b - a);
}
// Check if a rectangle is completly inside of another rectangle.
[[nodiscard]] inline auto rect_in_rect(const SDL_Rect& outer, const SDL_Rect& inner) noexcept
-> bool
{
@ -55,24 +64,28 @@ namespace bwidgets
&& (SDL_PointInRect(&bottom_right, &outer) == SDL_TRUE);
}
// Get the rectangle inside r leaving margins of given size between inner and outer rectangle.
[[nodiscard]] 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};
}
// Get the rectangle obtained from adding offset to its origin coordinate.
[[nodiscard]] 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};
}
// Commodity function to use another rectangle as a size for the offset to be added to r.
[[nodiscard]] 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});
}
// Standard smoothstep algorithm.
template<Numeric N>
[[nodiscard]] inline auto smoothstep(const N x, const N a, const N b) noexcept -> float
{

View File

@ -14,6 +14,7 @@ namespace bwidgets
{
class Texture;
// Wrap some of SDL's 2D accelerated rendering API.
class Renderer final : OpaqueStruct<SDL_Renderer>::Wrapper
{
friend Texture;

View File

@ -12,6 +12,7 @@ namespace bwidgets
{
class Renderer;
// Wrap most of the texture functions of SDL 2D rendering API.
class Texture final : OpaqueStruct<SDL_Texture>::Wrapper
{
friend Renderer;

View File

@ -10,6 +10,7 @@
namespace bwidgets
{
// Wrap SDL_Color to provide operator overloads.
class Color final
{
public:
@ -29,16 +30,21 @@ namespace bwidgets
Color(Color&&) noexcept = default;
~Color() = default;
[[nodiscard]] auto alpha() const noexcept -> Color
// Enable/disable operations on alpha channel. Disabled by default on
// construction.
[[nodiscard]] auto alpha(const bool state = true) const noexcept -> Color
{
return {sdl_type, _operate_on_colors, true};
return {sdl_type, _operate_on_colors, state};
}
[[nodiscard]] auto colors() const noexcept -> Color
// Enable/disable operations on color channels.Enabled by default on
// construction.
[[nodiscard]] auto colors(const bool state = true) const noexcept -> Color
{
return {sdl_type, true, _operate_on_alpha};
return {sdl_type, state, _operate_on_alpha};
}
// Get reference to the wrapped SDL native object.
[[nodiscard]] auto& operator()() noexcept
{
return sdl_type;
@ -49,24 +55,28 @@ namespace bwidgets
return sdl_type;
}
// Add operand to enabled channels.
template<Numeric N>
[[nodiscard]] auto operator+(const N operand) const noexcept
{
return _operate(operand, [](N a, N b) { return a + b; });
}
// Substract operand from enabled channels.
template<Numeric N>
[[nodiscard]] auto operator-(const N operand) const noexcept
{
return _operate(operand, [](N a, N b) { return a - b; });
}
// Multiply enabled channel values by opererand.
template<Numeric N>
[[nodiscard]] auto operator*(const N operand) const noexcept
{
return _operate(operand, [](N a, N b) { return a * b; });
}
// Divide enabled channel values by operand.
template<Numeric N>
[[nodiscard]] auto operator/(const N operand) const
{
@ -84,6 +94,8 @@ namespace bwidgets
return *this;
}
// Compare for equality of the enabled channel values between two Color
// instances.
[[nodiscard]] auto operator==(const Color other) const noexcept
{
return (_operate_on_colors && sdl_type.r == other().r
@ -91,6 +103,8 @@ namespace bwidgets
|| (_operate_on_alpha && sdl_type.a == other().a);
}
// Compare for inequality of the enabled channel values between two Color
// instances.
[[nodiscard]] auto operator!=(const Color other) const noexcept
{
return (sdl_type.r != other().r || sdl_type.g != other().g

View File

@ -6,18 +6,22 @@
namespace bwidgets
{
// T can be converted to string with to_string.
template<typename T>
concept CanToString = requires(T value)
{
std::to_string(value);
};
// T is a floating point type.
template<typename T>
concept FloatingPoint = std::is_floating_point_v<T>;
// T can be any numeric type.
template<typename T>
concept Numeric = std::is_arithmetic_v<T>;
// T can be converted to string by using ostream.
template<typename T>
concept Printable = requires(T value)
{

View File

@ -7,11 +7,14 @@
namespace bwidgets
{
struct BaseException : std::runtime_error
// Base type for custom runtime errors.
class BaseException : std::runtime_error
{
protected:
using runtime_error::runtime_error;
};
// T derives from BaseException.
template<typename T>
concept Exception = std::derived_from<T, BaseException>;
}

View File

@ -5,6 +5,7 @@
namespace bwidgets
{
// Exception type for fontconfig errors
struct FCError final : BaseException
{
using BaseException::BaseException;

View File

@ -5,6 +5,7 @@
namespace bwidgets
{
// Wrap opaque structures to be able to use a destructor to free them.
template<typename T>
class OpaqueStruct
{
@ -15,6 +16,8 @@ namespace bwidgets
T* const _c_pod;
public:
// Construct an instance managing resource pointed by ptr using Deleter d to free
// it on wrapper destruction.
OpaqueStruct(T* ptr, Deleter d) : _deleter {std::move(d)}, _c_pod {ptr} {}
OpaqueStruct(const OpaqueStruct&) = delete;
@ -25,6 +28,7 @@ namespace bwidgets
_deleter(_c_pod);
};
// Get the pointer to the wrapped opaque struct.
[[nodiscard]] auto* operator()() const
{
return _c_pod;
@ -33,9 +37,11 @@ namespace bwidgets
auto operator=(const OpaqueStruct&) = delete;
auto operator=(OpaqueStruct&&) = delete;
// Base class for defining custom wrappers.
class Wrapper
{
public:
// Forward args to OpaqueStruct ctor.
Wrapper(T* ptr, Deleter d) : _data(ptr, std::move(d)) {}
protected:

View File

@ -5,6 +5,7 @@
namespace bwidgets
{
// Custom exception type for SDL errors.
struct SDLError final : BaseException
{
using BaseException::BaseException;

View File

@ -5,22 +5,26 @@
namespace bwidgets
{
// Represent size of a 2D surface.
struct Size
{
int w;
int h;
};
// Addition the dimensions of two Size objects.
[[nodiscard]] inline auto operator+(const Size a, const Size b) noexcept -> Size
{
return {a.w + b.w, a.h + b.h};
}
// Substract dimensions of a Size object from another.
[[nodiscard]] inline auto operator-(const Size a, const Size b) noexcept -> Size
{
return {a.w - b.w, a.h - b.h};
}
// Multiply dimensions of a Size instance by a number.
template<Numeric N>
[[nodiscard]] inline auto operator*(const Size a, const N b) noexcept -> Size
{

View File

@ -11,6 +11,7 @@ namespace bwidgets
VERTICAL
};
// Align vertically or horizontally widgets.
class AlignedLayout : public virtual Layout
{
public:

View File

@ -35,16 +35,23 @@ namespace bwidgets
char input_width_unit = 'W';
T value {};
virtual void color_fg(Color c) = 0;
// Get the current displayed string.
[[nodiscard]] virtual auto input_text() const -> std::string_view = 0;
virtual void input_text(std::string txt) = 0;
// Set displayed string
virtual void input_text(std::string) = 0;
virtual void input_text_color(Color) = 0;
// Check if a character is allowed to be inputed. String is used because we could
// have multibytes characters to represent using 8bits chars.
[[nodiscard]] virtual auto is_valid_input(std::string_view) const noexcept
-> bool = 0;
[[nodiscard]] virtual auto process_value(T x) const noexcept -> T = 0;
[[nodiscard]] virtual auto value_from_string(std::string_view s) const noexcept
-> T = 0;
[[nodiscard]] virtual auto value_to_string(T value) const noexcept
-> std::string = 0;
-> bool = 0;
// Process a value for instance to modify it before saving it.
[[nodiscard]] virtual auto process_value(T) const noexcept -> T = 0;
// Get string representation to be shown of the stored value.
[[nodiscard]] virtual auto value_from_string(std::string_view) const noexcept
-> T = 0;
// Parse a string to get its representation in T type of input value memory.
[[nodiscard]] virtual auto value_to_string(T) const noexcept -> std::string = 0;
};
}

View File

@ -206,7 +206,9 @@ namespace bwidgets
{
const auto& color_end =
focus() ? InputImpl::color_bg_focused : InputImpl::color_bg_default;
const auto color_start = color_end / 2;
// Render input borders.
for (int i = InputImpl::border_width - 1; i >= 0; i--) {
const auto factor = linear(i, 0, InputImpl::border_width);
_renderer->draw_color(lerp(color_start, color_end, factor))

View File

@ -11,11 +11,13 @@ namespace bwidgets
public:
static inline const Size default_margins {8, 8};
// Margins between layout widgets.
Size margins = default_margins;
using Widget::Widget;
// Add widget to the layout
virtual void add_widget(std::unique_ptr<Widget>) = 0;
// Apply a function to every layout widget.
virtual void for_widgets(const std::function<void(Widget*)>&) = 0;
};
}

View File

@ -30,6 +30,7 @@ namespace bwidgets
void _handle_renderer_change(const std::shared_ptr<Renderer>&) override;
void _handle_rendering() override;
// Compute and set widget geometry.
virtual void _update_layout(const SDL_Rect&) = 0;
};
}

View File

@ -16,14 +16,21 @@ namespace bwidgets
class Widget : public virtual EventHandler
{
public:
// Parent widget for bidirectional message passing between widgets.
Widget* parent;
explicit Widget(Widget* p = nullptr) noexcept : parent {p} {}
// Render widget on current renderer target.
virtual void render() = 0;
// Set widget renderer.
virtual void renderer(std::shared_ptr<Renderer>) = 0;
// Get prefered or minimal widget size.
[[nodiscard]] virtual auto size() const noexcept -> Size = 0;
// Set the viewport delimiting the widget rendering area and origin
// for relative coordinates.
virtual void viewport(const SDL_Rect&) = 0;
// Get current viewport.
[[nodiscard]] virtual auto viewport() const -> const SDL_Rect& = 0;
};
}

View File

@ -18,12 +18,16 @@ namespace bwidgets
protected:
std::shared_ptr<Renderer> _renderer;
SDL_Rect _viewport {0, 0, 0, 0};
// Area actually used by the widget.
SDL_Rect _widget_area {0, 0, 0, 0};
using Widget::Widget;
// Called on viewport change.
virtual void _handle_geometry_change(const SDL_Rect&) = 0;
// Called on renderer change.
virtual void _handle_renderer_change(const std::shared_ptr<Renderer>&) {}
// Called on rendering.
virtual void _handle_rendering() = 0;
};
}

View File

@ -27,7 +27,9 @@ namespace bwidgets
using Widget::Widget;
// Get button text (label).
[[nodiscard]] virtual auto text() const noexcept -> std::string_view = 0;
// Set button text
virtual void text(std::string) = 0;
};
}

View File

@ -24,13 +24,17 @@ namespace bwidgets
inline static const Color default_color_fg = {0, 0, 0, SDL_ALPHA_OPAQUE};
inline static const Size default_margins = {3, 3};
// How text is horizontally aligned on the rendering viewport.
Alignment alignment {Alignment::LEFT};
Size margins {default_margins};
using Widget::Widget;
// Set used font rendering mode.
virtual void render_mode(Font::RenderMode) = 0;
// Get caption text.
[[nodiscard]] virtual auto text() const noexcept -> std::string_view = 0;
// Set caption text.
virtual void text(std::string) = 0;
};
}

View File

@ -22,6 +22,7 @@ namespace bwidgets
virtual ~EventHandler() = default;
// Pass an event to be handled.
virtual void handle_event(const SDL_Event&) = 0;
};
}

View File

@ -16,7 +16,13 @@ namespace bwidgets
void handle_event(const SDL_Event&) override;
protected:
// Add an handler for a given event type. There can be only one handler by
// event type. Return true if handler is added successfuly. Return false
// if handler cannot be added because there's already an handler for that
// event type.
auto _add_event_handler(handler_t) -> bool;
// Remove the handler of a given event type. Return true with the removed
// handler on success, false otherwise.
auto _remove_event_handler(SDL_EventType) -> std::pair<handler_t, bool>;
};
}

View File

@ -17,8 +17,11 @@ namespace bwidgets
auto operator=(const FocusHandler&) = delete;
virtual ~FocusHandler() = default;
// set focus state.
virtual void focus(bool focus) = 0;
// get focus state.
virtual bool focus() = 0;
// set an handler for focus states changes.
virtual void focus_handler(std::function<void(bool)>) = 0;
};
}

View File

@ -19,8 +19,12 @@ namespace bwidgets
auto operator=(FontHandler&&) = delete;
virtual ~FontHandler() noexcept = default;
// Set the used font.
virtual void font(std::shared_ptr<Font> f) = 0;
// Set font background color for shaded font rendering
// mode
virtual void font_color_bg(Color c) = 0;
// Set font foreground (text) color.
virtual void font_color_fg(Color c) = 0;
};
}

View File

@ -44,7 +44,9 @@ namespace bwidgets
Color _font_color_fg = default_font_color_fg;
Font::RenderMode _font_render_mode {Font::RenderMode::SHADED};
// Called on font changes.
virtual void _handle_font_change(const std::shared_ptr<Font>&) = 0;
// Called on font color changes.
virtual void _handle_font_color_change(Color, Color) = 0;
};
}

View File

@ -16,9 +16,13 @@ namespace bwidgets
using EventHandler::EventHandler;
public:
// Disable keyboard event handling.
virtual void disable_keyboard_handler() = 0;
// Enable keyboard event handling.
virtual void enable_keyboard_handler() = 0;
// Set the handler for key press events.
virtual void key_handler(std::function<void(const SDL_KeyboardEvent&)>) = 0;
// Set the handler for text input events.
virtual void
text_input_handler(std::function<void(const SDL_TextInputEvent&)>) = 0;
};

View File

@ -15,16 +15,21 @@ namespace bwidgets
protected:
using EventHandler::EventHandler;
virtual void _on_push(bool) = 0;
public:
// Set click handler.
virtual void click_handler(std::function<void(const SDL_MouseButtonEvent&)>) = 0;
// Disable mouse event handling.
virtual void disable_mouse_handler() = 0;
// Enable mouse event handling.
virtual void enable_mouse_handler(const SDL_Rect&, const SDL_Rect&) = 0;
// Called on hover state changes.
virtual void hover_handler(std::function<void(bool)>) = 0;
// Get current hover state.
[[nodiscard]] virtual bool hovered() const = 0;
// Set mouse motion handler.
virtual void
mouse_motion_handler(std::function<void(const SDL_MouseMotionEvent&)>) = 0;
// Get current push state (mouse button down)
[[nodiscard]] virtual bool pushed() const = 0;
};
}

View File

@ -51,6 +51,7 @@ namespace bwidgets
protected:
using MouseHandler::MouseHandler;
// Called on push state changes.
void _on_push(bool) override {}
};
}

View File

@ -11,6 +11,7 @@ namespace bwidgets
class TextureHandler
{
protected:
// (Re)render textures.
virtual void _handle_texture_update() = 0;
public:

View File

@ -10,12 +10,15 @@ namespace bwidgets
class NumericInput : public virtual Input<T>
{
public:
// Button increment/decrement step.
T button_step = 1;
using Input<T>::Input;
// Get allowed value range.
[[nodiscard]] virtual auto value_range() const noexcept
-> const std::pair<T, T>& = 0;
// Set allowed value range.
virtual void value_range(T min, T max) = 0;
};
}

View File

@ -23,15 +23,20 @@ namespace bwidgets
Self::input_min_width = 10; // NOLINT(readability-magic-numbers)
Self::input_width_unit = '0';
// +/- buttons click handler. op is the function to apply on current value.
const auto button_action = [this](const std::function<T(T, T)> op) {
const T new_value = [this, &op]() {
auto _new_value = op(this->value, NumericInput<T>::button_step);
if (_new_value < this->value) {
// if value - button_step is out of allowed range
// set value to the smallest allowed value.
if (std::abs(_value_range.first - this->value)
< std::abs(NumericInput<T>::button_step))
return _value_range.first;
return _new_value;
}
// if value + button_step exceeds maximal allowed value
// set value to max allowed value.
if (std::abs(_value_range.second - this->value)
< std::abs(NumericInput<T>::button_step))
return _value_range.second;
@ -67,11 +72,14 @@ namespace bwidgets
-> bool override
{
bool valid = [input, this]() {
// Allow 0-9 chars.
if (input[0] >= '0' && input[0] <= '9') return true;
// Allow one point for float types.
if constexpr (std::is_floating_point_v<T>)
if (input[0] == '.'
&& Self::input_text().find('.') == std::string::npos)
return true;
// Allow "-" at input string start for signed types.
if constexpr (std::is_signed_v<T>) {
std::string_view displayed_value = Self::input_text();
if (input[0] == '-' && displayed_value.empty()) return true;
@ -83,6 +91,8 @@ namespace bwidgets
[[nodiscard]] auto process_value(const T x) const noexcept -> T override
{
// If value is out of allowed range return either min or max allowed
// value.
T value = [x, this]() {
if (x < _value_range.first) return _value_range.first;
if (x > _value_range.second) return _value_range.second;

View File

@ -63,6 +63,7 @@ namespace bwidgets
std::vector<uint32_t> pixels;
pixels.reserve(attr.h * (std::vector<uint32_t>::size_type)pitch);
// TODO parallel algo
for (auto y = 0; y < attr.h; y++) {
for (auto x = 0; x < attr.w; x++) {
pixels.emplace_back(pixel_color({x, y}, *attr.format));

View File

@ -7,6 +7,7 @@ void WidgetImpl::render()
if (!_renderer) return;
#ifdef BWIDGETS_DEBUG
// Render a debug outline of widget viewport.
_renderer
->draw_color({
// NOLINTNEXTLINE(readability-magic-numbers)