Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions include/asw/modules/ui/button.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,32 @@ class Button : public Widget {
///
void draw(Context& ctx) override;

/// @brief Padding applied inside the button on all sides.
float padding = 0.0f;

/// @brief The font to use for the button text.
asw::Font font;

/// @brief The button text.
std::string text;

private:
bool hovered_ = false;
bool pressed_ = false;
bool focused_ = false;
/// @brief The texture to display on the button.
asw::Texture texture;

/// @brief Set the texture, optionally resizing the button to match.
///
/// @param tex The texture to set.
/// @param auto_size If true, resizes the button to the texture dimensions.
///
void set_texture(const asw::Texture& tex, bool auto_size = false);

/// @brief Set the text, optionally resizing the button to match.
///
/// @param t The text to set.
/// @param auto_size If true, resizes the button to fit the text.
///
void set_text(const std::string& t, bool auto_size = false);

};

} // namespace asw::ui
Expand Down
6 changes: 3 additions & 3 deletions include/asw/modules/ui/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class FocusManager {
///
Widget* focused() const
{
return focused_;
return _focused;
}

/// @brief Set focus to a specific widget.
Expand Down Expand Up @@ -66,8 +66,8 @@ class FocusManager {
private:
void dfs(Widget& w);

std::vector<Widget*> focusables_;
Widget* focused_ = nullptr;
std::vector<Widget*> _focusables;
Widget* _focused = nullptr;
};

/// @brief Shared state for the UI system.
Expand Down
4 changes: 1 addition & 3 deletions include/asw/modules/ui/input_box.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ class InputBox : public Widget {
std::string placeholder;

private:
bool hovered_ = false;
bool focused_ = false;
std::size_t cursor_pos_ = 0;
std::size_t _cursor_pos = 0;
};

} // namespace asw::ui
Expand Down
24 changes: 19 additions & 5 deletions include/asw/modules/ui/widget.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@

/// @brief Base class for all UI widgets.
///
class Widget {

Check failure on line 28 in include/asw/modules/ui/widget.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Member variables should not be "protected".

See more on https://sonarcloud.io/project/issues?id=AdsGames_asw&issues=AZ0ITWjqjy-9gvRaRt5c&open=AZ0ITWjqjy-9gvRaRt5c&pullRequest=41
public:
/// @brief Default constructor for Widget.
///
Widget()
: id_(generate_id()) { };
: _id(generate_id()) { };

Check warning on line 33 in include/asw/modules/ui/widget.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not use the constructor's initializer list for data member "_id". Use the in-class initializer instead.

See more on https://sonarcloud.io/project/issues?id=AdsGames_asw&issues=AZ0ITWjqjy-9gvRaRt5b&open=AZ0ITWjqjy-9gvRaRt5b&pullRequest=41

/// @brief Default virtual destructor.
///
Expand All @@ -53,7 +53,7 @@
///
WidgetId id() const
{
return id_;
return _id;
}

/// @brief Whether the widget is visible.
Expand Down Expand Up @@ -118,15 +118,29 @@
/// @brief The transform (position and size) of the widget.
asw::Quad<float> transform;

/// @brief Whether the pointer is currently over this widget.
bool is_hovered() const { return _hovered; }

/// @brief Whether this widget is currently being pressed.
bool is_pressed() const { return _pressed; }

/// @brief Whether this widget currently holds focus.
bool is_focused() const { return _focused; }

protected:
bool _hovered = false;
bool _pressed = false;
bool _focused = false;

private:
static inline int id_counter_ { 1 };
static inline int _id_counter { 1 };

static int generate_id()
{
return id_counter_++;
return _id_counter++;
}

WidgetId id_;
WidgetId _id;
};

} // namespace asw::ui
Expand Down
1 change: 1 addition & 0 deletions src/modules/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ void asw::core::cleanup()
SDL_DestroyWindow(asw::display::window);
asw::display::window = nullptr;
}

MIX_Quit();
TTF_Quit();
SDL_Quit();
Expand Down
4 changes: 2 additions & 2 deletions src/modules/draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,11 @@ void asw::draw::text(const asw::Font& font, const std::string& text,
}

const auto sdlColor = SDL_Color { color.r, color.g, color.b, color.a };
SDL_Surface* textSurface = TTF_RenderText_Solid(font.get(), text.c_str(), 0, sdlColor);
SDL_Surface* textSurface = TTF_RenderText_Blended(font.get(), text.c_str(), 0, sdlColor);
SDL_Texture* textTexture = SDL_CreateTextureFromSurface(asw::display::renderer, textSurface);

SDL_SetTextureBlendMode(textTexture, SDL_BLENDMODE_BLEND);
SDL_SetTextureScaleMode(textTexture, SDL_SCALEMODE_NEAREST);
SDL_SetTextureScaleMode(textTexture, SDL_SCALEMODE_LINEAR);

SDL_FRect dest;
dest.x = position.x;
Expand Down
51 changes: 40 additions & 11 deletions src/modules/ui/button.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

void asw::ui::Button::on_focus_changed(Context& ctx, bool focused)
{
focused_ = focused;
_focused = focused;
(void)ctx;
}

Expand All @@ -17,20 +17,20 @@ bool asw::ui::Button::on_event(Context& ctx, const UIEvent& e)

switch (e.type) {
case UIEvent::Type::PointerEnter: {
hovered_ = true;
_hovered = true;
return false;
}
case UIEvent::Type::PointerLeave: {
hovered_ = false;
pressed_ = false;
_hovered = false;
_pressed = false;
return false;
}
case UIEvent::Type::PointerMove: {
return false;
}
case UIEvent::Type::PointerDown: {
if (transform.contains(e.pointer_pos)) {
pressed_ = true;
_pressed = true;
ctx.pointer_capture = this;
ctx.focus.set_focus(ctx, this);
return true;
Expand All @@ -39,8 +39,8 @@ bool asw::ui::Button::on_event(Context& ctx, const UIEvent& e)
}
case UIEvent::Type::PointerUp: {
const bool in = transform.contains(e.pointer_pos);
const bool wasPressed = pressed_;
pressed_ = false;
const bool wasPressed = _pressed;
_pressed = false;
if (ctx.pointer_capture == this) {
ctx.pointer_capture = nullptr;
}
Expand All @@ -64,28 +64,57 @@ bool asw::ui::Button::on_event(Context& ctx, const UIEvent& e)
return false;
}

void asw::ui::Button::set_texture(const asw::Texture& tex, bool auto_size)
{
texture = tex;
if (auto_size && texture != nullptr) {
const auto tex_size = asw::util::get_texture_size(texture);
transform.size = tex_size + asw::Vec2<float>(padding * 2.0f, padding * 2.0f);
}
}

void asw::ui::Button::set_text(const std::string& t, bool auto_size)
{
text = t;
if (auto_size && font != nullptr && !text.empty()) {
const auto size = asw::util::get_text_size(font, text);
transform.size = asw::Vec2<float>(
size.x + padding * 2.0f,
size.y + padding * 2.0f);
}
}

void asw::ui::Button::draw(Context& ctx)
{
asw::Color bg = ctx.theme.btn_bg;
if (!enabled) {
bg = ctx.theme.panel_bg;
} else if (pressed_) {
} else if (_pressed) {
bg = ctx.theme.btn_pressed;
} else if (hovered_) {
} else if (_hovered) {
bg = ctx.theme.btn_hover;
}

asw::draw::rect_fill(transform, bg);

const asw::Quad<float> inner {
{ transform.position.x + padding, transform.position.y + padding },
{ transform.size.x - padding * 2.0f, transform.size.y - padding * 2.0f }
};

if (texture != nullptr) {
asw::draw::stretch_sprite(texture, inner);
}

if (!text.empty() && font != nullptr) {
const auto text_size = asw::util::get_text_size(font, text);
const auto text_pos
= transform.get_center() - asw::Vec2<float>(text_size.x / 2.0f, text_size.y / 2.0f);
= inner.get_center() - asw::Vec2<float>(text_size.x / 2.0f, text_size.y / 2.0f);

asw::draw::text(font, text, text_pos, ctx.theme.text, asw::TextJustify::Left);
}

if (focused_ && ctx.theme.show_focus) {
if (_focused && ctx.theme.show_focus) {
// Focus ring
auto ring = asw::Quad<float>(transform);
ring.position.x -= 2;
Expand Down
70 changes: 35 additions & 35 deletions src/modules/ui/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,100 +5,100 @@

void asw::ui::FocusManager::rebuild(Context& ctx, Widget& root)
{
focusables_.clear();
_focusables.clear();
dfs(root);
// Keep current focus if still exists
if (focused_ != nullptr) {
auto it = std::ranges::find(focusables_, focused_);
if (it == focusables_.end()) {
if (_focused != nullptr) {
auto it = std::ranges::find(_focusables, _focused);
if (it == _focusables.end()) {
set_focus(ctx, nullptr);
}
}
if (focused_ == nullptr && !focusables_.empty()) {
set_focus(ctx, focusables_.front());
if (_focused == nullptr && !_focusables.empty()) {
set_focus(ctx, _focusables.front());
}
}

void asw::ui::FocusManager::set_focus(Context& ctx, Widget* w)
{
if (focused_ == w) {
if (_focused == w) {
return;
}
if (focused_ != nullptr) {
focused_->on_focus_changed(ctx, false);
if (_focused != nullptr) {
_focused->on_focus_changed(ctx, false);
}
focused_ = w;
if (focused_ != nullptr) {
focused_->on_focus_changed(ctx, true);
_focused = w;
if (_focused != nullptr) {
_focused->on_focus_changed(ctx, true);
}
}

void asw::ui::FocusManager::focus_next(Context& ctx)
{
if (focusables_.empty()) {
if (_focusables.empty()) {
return;
}

if (focused_ == nullptr) {
set_focus(ctx, focusables_.front());
if (_focused == nullptr) {
set_focus(ctx, _focusables.front());
return;
}
auto it = std::ranges::find(focusables_, focused_);
if (it == focusables_.end()) {
set_focus(ctx, focusables_.front());
auto it = std::ranges::find(_focusables, _focused);
if (it == _focusables.end()) {
set_focus(ctx, _focusables.front());
return;
}
++it;
if (it == focusables_.end()) {
it = focusables_.begin();
if (it == _focusables.end()) {
it = _focusables.begin();
}
set_focus(ctx, *it);
}

void asw::ui::FocusManager::focus_prev(Context& ctx)
{
if (focusables_.empty()) {
if (_focusables.empty()) {
return;
}

if (focused_ == nullptr) {
set_focus(ctx, focusables_.front());
if (_focused == nullptr) {
set_focus(ctx, _focusables.front());
return;
}

auto it = std::ranges::find(focusables_, focused_);
if (it == focusables_.end()) {
set_focus(ctx, focusables_.front());
auto it = std::ranges::find(_focusables, _focused);
if (it == _focusables.end()) {
set_focus(ctx, _focusables.front());
return;
}

if (it == focusables_.begin()) {
it = focusables_.end();
if (it == _focusables.begin()) {
it = _focusables.end();
}
--it;
set_focus(ctx, *it);
}

void asw::ui::FocusManager::focus_dir(Context& ctx, int dx, int dy)
{
if (focusables_.empty()) {
if (_focusables.empty()) {
return;
}

if (focused_ == nullptr) {
set_focus(ctx, focusables_.front());
if (_focused == nullptr) {
set_focus(ctx, _focusables.front());
return;
}

const auto& from = focused_->transform;
const auto& from = _focused->transform;
const float fx = from.get_center().x;
const float fy = from.get_center().y;

Widget* best = nullptr;
float bestScore = 1e30f;

for (Widget* w : focusables_) {
if (w == focused_) {
for (Widget* w : _focusables) {
if (w == _focused) {
continue;
}
const auto& to = w->transform;
Expand Down Expand Up @@ -138,7 +138,7 @@ void asw::ui::FocusManager::focus_dir(Context& ctx, int dx, int dy)
void asw::ui::FocusManager::dfs(Widget& w)
{
if (w.visible && w.enabled && w.focusable) {
focusables_.push_back(&w);
_focusables.push_back(&w);
}
for (auto const& c : w.children) {
dfs(*c);
Expand Down
Loading
Loading