Add radio button with simple/advanced view, add widget visibility

This commit is contained in:
dec05eba
2024-08-07 00:21:32 +02:00
parent b229b060ad
commit a3e479d5b2
18 changed files with 404 additions and 24 deletions

2
TODO
View File

@@ -15,3 +15,5 @@ Optimize list/page when having a few items in it (dont use vector<unique_ptr<Wid
Only redraw ui if changed (dirty state, propagate upward. Set dirty when adding widget or changing any visible properly on a widget or when event updates how the widget should be displayed). Only redraw ui if changed (dirty state, propagate upward. Set dirty when adding widget or changing any visible properly on a widget or when event updates how the widget should be displayed).
Use _NET_WM_ALLOWED_ACTIONS. Same for notifications. Use _NET_WM_ALLOWED_ACTIONS. Same for notifications.
Handle events in draw function because the render position of elements is available there so why duplicate it in event handler.

View File

@@ -0,0 +1,39 @@
#pragma once
#include "Widget.hpp"
#include <mglpp/graphics/Text.hpp>
#include <vector>
#include <functional>
namespace gsr {
class RadioButton : public Widget {
public:
RadioButton(mgl::Font *font);
RadioButton(const RadioButton&) = delete;
RadioButton& operator=(const RadioButton&) = delete;
bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override;
void draw(mgl::Window &window, mgl::vec2f offset) override;
void add_item(const std::string &text, const std::string &id);
void set_selected_item(const std::string &id);
mgl::vec2f get_size() override;
std::function<void(const std::string &text, const std::string &id)> on_selection_changed;
private:
void update_if_dirty();
private:
struct Item {
mgl::Text text;
std::string id;
};
mgl::Font *font;
std::vector<Item> items;
size_t selected_item = 0;
bool dirty = true;
mgl::vec2f size;
};
}

View File

@@ -12,10 +12,16 @@ namespace gsr {
bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override; bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override;
void draw(mgl::Window &window, mgl::vec2f offset) override; void draw(mgl::Window &window, mgl::vec2f offset) override;
mgl::vec2f get_size() override { return size; } mgl::vec2f get_size() override;
void set_margins(float top, float bottom, float left, float right);
private: private:
float get_border_size(mgl::Window &window) const; float get_border_size() const;
private: private:
mgl::vec2f size; mgl::vec2f size;
float margin_top_scale = 0.0f;
float margin_bottom_scale = 0.0f;
float margin_left_scale = 0.0f;
float margin_right_scale = 0.0f;
}; };
} }

View File

@@ -12,7 +12,7 @@ namespace gsr {
bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override; bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override;
void draw(mgl::Window &window, mgl::vec2f offset) override; void draw(mgl::Window &window, mgl::vec2f offset) override;
mgl::vec2f get_size() override { return size; } mgl::vec2f get_size() override;
private: private:
mgl::vec2f size; mgl::vec2f size;
}; };

View File

@@ -14,6 +14,12 @@ namespace gsr {
friend class List; friend class List;
friend class Page; friend class Page;
public: public:
enum class Alignment {
START,
CENTER,
END
};
Widget(); Widget();
Widget(const Widget&) = delete; Widget(const Widget&) = delete;
Widget& operator=(const Widget&) = delete; Widget& operator=(const Widget&) = delete;
@@ -28,12 +34,26 @@ namespace gsr {
virtual mgl::vec2f get_position() const; virtual mgl::vec2f get_position() const;
virtual mgl::vec2f get_size() = 0; virtual mgl::vec2f get_size() = 0;
void set_horizontal_alignment(Alignment alignment);
void set_vertical_alignment(Alignment alignment);
Alignment get_horizontal_alignment() const;
Alignment get_vertical_alignment() const;
void set_visible(bool visible);
protected: protected:
void set_widget_as_selected_in_parent(); void set_widget_as_selected_in_parent();
void remove_widget_as_selected_in_parent(); void remove_widget_as_selected_in_parent();
bool has_parent_with_selected_child_widget() const;
protected: protected:
mgl::vec2f position; mgl::vec2f position;
Widget *parent_widget = nullptr; Widget *parent_widget = nullptr;
Widget *selected_child_widget = nullptr; Widget *selected_child_widget = nullptr;
Alignment horizontal_aligment;
Alignment vertical_aligment;
bool visible = true;
}; };
} }

View File

@@ -10,6 +10,7 @@ src = [
'src/Theme.cpp', 'src/Theme.cpp',
'src/gui/ScrollablePage.cpp', 'src/gui/ScrollablePage.cpp',
'src/gui/Button.cpp', 'src/gui/Button.cpp',
'src/gui/RadioButton.cpp',
'src/gui/Entry.cpp', 'src/gui/Entry.cpp',
'src/gui/CheckBox.cpp', 'src/gui/CheckBox.cpp',
'src/gui/ComboBox.cpp', 'src/gui/ComboBox.cpp',

View File

@@ -17,6 +17,9 @@ namespace gsr {
} }
bool Button::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) { bool Button::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) {
if(!visible)
return true;
const mgl::vec2f item_size = get_size().floor(); const mgl::vec2f item_size = get_size().floor();
if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) { if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) {
const bool clicked_inside = mgl::FloatRect(position + offset, item_size).contains({ (float)event.mouse_button.x, (float)event.mouse_button.y }); const bool clicked_inside = mgl::FloatRect(position + offset, item_size).contains({ (float)event.mouse_button.x, (float)event.mouse_button.y });
@@ -27,6 +30,9 @@ namespace gsr {
} }
void Button::draw(mgl::Window &window, mgl::vec2f offset) { void Button::draw(mgl::Window &window, mgl::vec2f offset) {
if(!visible)
return;
const mgl::vec2f draw_pos = position + offset; const mgl::vec2f draw_pos = position + offset;
const mgl::vec2f item_size = get_size().floor(); const mgl::vec2f item_size = get_size().floor();
@@ -38,12 +44,15 @@ namespace gsr {
text.set_position((draw_pos + item_size * 0.5f - text.get_bounds().size * 0.5f).floor()); text.set_position((draw_pos + item_size * 0.5f - text.get_bounds().size * 0.5f).floor());
window.draw(text); window.draw(text);
const bool mouse_inside = mgl::FloatRect(draw_pos, item_size).contains(window.get_mouse_position().to_vec2f()); const bool mouse_inside = mgl::FloatRect(draw_pos, item_size).contains(window.get_mouse_position().to_vec2f()) && !has_parent_with_selected_child_widget();
if(mouse_inside) if(mouse_inside)
draw_rectangle_outline(window, draw_pos, item_size, get_theme().tint_color, border_scale * get_theme().window_height); draw_rectangle_outline(window, draw_pos, item_size, get_theme().tint_color, border_scale * get_theme().window_height);
} }
mgl::vec2f Button::get_size() { mgl::vec2f Button::get_size() {
if(!visible)
return {0.0f, 0.0f};
const int padding_top = padding_top_scale * get_theme().window_height; const int padding_top = padding_top_scale * get_theme().window_height;
const int padding_bottom = padding_bottom_scale * get_theme().window_height; const int padding_bottom = padding_bottom_scale * get_theme().window_height;
const int padding_left = padding_left_scale * get_theme().window_height; const int padding_left = padding_left_scale * get_theme().window_height;

View File

@@ -16,6 +16,9 @@ namespace gsr {
} }
bool CheckBox::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) { bool CheckBox::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) {
if(!visible)
return true;
if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) { if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) {
const bool clicked_inside = mgl::FloatRect(position + offset, get_size()).contains({ (float)event.mouse_button.x, (float)event.mouse_button.y }); const bool clicked_inside = mgl::FloatRect(position + offset, get_size()).contains({ (float)event.mouse_button.x, (float)event.mouse_button.y });
if(clicked_inside) if(clicked_inside)
@@ -25,6 +28,9 @@ namespace gsr {
} }
void CheckBox::draw(mgl::Window &window, mgl::vec2f offset) { void CheckBox::draw(mgl::Window &window, mgl::vec2f offset) {
if(!visible)
return;
const mgl::vec2f draw_pos = position + offset; const mgl::vec2f draw_pos = position + offset;
const mgl::vec2f checkbox_size = get_checkbox_size(); const mgl::vec2f checkbox_size = get_checkbox_size();
@@ -45,7 +51,7 @@ namespace gsr {
text.set_position((draw_pos + mgl::vec2f(checkbox_size.x + spacing_scale * get_theme().window_height, checkbox_size.y * 0.5f - text_bounds.y * 0.5f)).floor()); text.set_position((draw_pos + mgl::vec2f(checkbox_size.x + spacing_scale * get_theme().window_height, checkbox_size.y * 0.5f - text_bounds.y * 0.5f)).floor());
window.draw(text); window.draw(text);
const bool mouse_inside = mgl::FloatRect(draw_pos, get_size()).contains(window.get_mouse_position().to_vec2f()); const bool mouse_inside = mgl::FloatRect(draw_pos, get_size()).contains(window.get_mouse_position().to_vec2f()) && !has_parent_with_selected_child_widget();
if(mouse_inside) { if(mouse_inside) {
const int border_size = std::max(1.0f, border_scale * get_theme().window_height); const int border_size = std::max(1.0f, border_scale * get_theme().window_height);
const mgl::Color border_color = get_theme().tint_color; const mgl::Color border_color = get_theme().tint_color;
@@ -54,6 +60,9 @@ namespace gsr {
} }
mgl::vec2f CheckBox::get_size() { mgl::vec2f CheckBox::get_size() {
if(!visible)
return {0.0f, 0.0f};
mgl::vec2f size = text.get_bounds().size; mgl::vec2f size = text.get_bounds().size;
const mgl::vec2f checkbox_size = get_checkbox_size(); const mgl::vec2f checkbox_size = get_checkbox_size();
size.x += checkbox_size.x + spacing_scale * get_theme().window_height; size.x += checkbox_size.x + spacing_scale * get_theme().window_height;

View File

@@ -19,6 +19,9 @@ namespace gsr {
} }
bool ComboBox::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) { bool ComboBox::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) {
if(!visible)
return true;
const int padding_top = padding_top_scale * get_theme().window_height; const int padding_top = padding_top_scale * get_theme().window_height;
const int padding_bottom = padding_bottom_scale * get_theme().window_height; const int padding_bottom = padding_bottom_scale * get_theme().window_height;
const int padding_left = padding_left_scale * get_theme().window_height; const int padding_left = padding_left_scale * get_theme().window_height;
@@ -60,6 +63,9 @@ namespace gsr {
} }
void ComboBox::draw(mgl::Window &window, mgl::vec2f offset) { void ComboBox::draw(mgl::Window &window, mgl::vec2f offset) {
if(!visible)
return;
update_if_dirty(); update_if_dirty();
if(items.empty()) if(items.empty())
@@ -92,7 +98,7 @@ namespace gsr {
window.draw(dropdown_arrow); window.draw(dropdown_arrow);
} }
const bool mouse_inside = mgl::FloatRect(draw_pos, item_size).contains(window.get_mouse_position().to_vec2f()); const bool mouse_inside = mgl::FloatRect(draw_pos, item_size).contains(window.get_mouse_position().to_vec2f()) && !has_parent_with_selected_child_widget();
mgl::vec2f pos = draw_pos + mgl::vec2f(padding_left, padding_top); mgl::vec2f pos = draw_pos + mgl::vec2f(padding_left, padding_top);
Item &item = items[selected_item]; Item &item = items[selected_item];
@@ -164,6 +170,9 @@ namespace gsr {
} }
mgl::vec2f ComboBox::get_size() { mgl::vec2f ComboBox::get_size() {
if(!visible)
return {0.0f, 0.0f};
update_if_dirty(); update_if_dirty();
const int padding_top = padding_top_scale * get_theme().window_height; const int padding_top = padding_top_scale * get_theme().window_height;

View File

@@ -26,6 +26,9 @@ namespace gsr {
} }
bool DropdownButton::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) { bool DropdownButton::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) {
if(!visible)
return true;
if(event.type == mgl::Event::MouseMoved) { if(event.type == mgl::Event::MouseMoved) {
const mgl::vec2f draw_pos = position + offset; const mgl::vec2f draw_pos = position + offset;
const mgl::vec2f collision_margin(1.0f, 1.0f); // Makes sure that multiple buttons that are next to each other wont activate at the same time when the cursor is right between them const mgl::vec2f collision_margin(1.0f, 1.0f); // Makes sure that multiple buttons that are next to each other wont activate at the same time when the cursor is right between them
@@ -56,6 +59,9 @@ namespace gsr {
} }
void DropdownButton::draw(mgl::Window &window, mgl::vec2f offset) { void DropdownButton::draw(mgl::Window &window, mgl::vec2f offset) {
if(!visible)
return;
update_if_dirty(); update_if_dirty();
const mgl::vec2f draw_pos = position + offset; const mgl::vec2f draw_pos = position + offset;
@@ -199,6 +205,9 @@ namespace gsr {
} }
mgl::vec2f DropdownButton::get_size() { mgl::vec2f DropdownButton::get_size() {
if(!visible)
return {0.0f, 0.0f};
update_if_dirty(); update_if_dirty();
return size; return size;
} }

View File

@@ -22,6 +22,9 @@ namespace gsr {
} }
bool Entry::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) { bool Entry::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) {
if(!visible)
return true;
if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) { if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) {
selected = mgl::FloatRect(position + offset, get_size()).contains({ (float)event.mouse_button.x, (float)event.mouse_button.y }); selected = mgl::FloatRect(position + offset, get_size()).contains({ (float)event.mouse_button.x, (float)event.mouse_button.y });
} else if(event.type == mgl::Event::KeyPressed && selected) { } else if(event.type == mgl::Event::KeyPressed && selected) {
@@ -40,6 +43,9 @@ namespace gsr {
} }
void Entry::draw(mgl::Window &window, mgl::vec2f offset) { void Entry::draw(mgl::Window &window, mgl::vec2f offset) {
if(!visible)
return;
const mgl::vec2f draw_pos = position + offset; const mgl::vec2f draw_pos = position + offset;
const int padding_top = padding_top_scale * get_theme().window_height; const int padding_top = padding_top_scale * get_theme().window_height;
@@ -66,6 +72,9 @@ namespace gsr {
} }
mgl::vec2f Entry::get_size() { mgl::vec2f Entry::get_size() {
if(!visible)
return {0.0f, 0.0f};
const int padding_top = padding_top_scale * get_theme().window_height; const int padding_top = padding_top_scale * get_theme().window_height;
const int padding_bottom = padding_bottom_scale * get_theme().window_height; const int padding_bottom = padding_bottom_scale * get_theme().window_height;
return { max_width, text.get_bounds().size.y + padding_top + padding_bottom }; return { max_width, text.get_bounds().size.y + padding_top + padding_bottom };

View File

@@ -11,11 +11,17 @@ namespace gsr {
} }
void Label::draw(mgl::Window &window, mgl::vec2f offset) { void Label::draw(mgl::Window &window, mgl::vec2f offset) {
if(!visible)
return;
text.set_position((position + offset).floor()); text.set_position((position + offset).floor());
window.draw(text); window.draw(text);
} }
mgl::vec2f Label::get_size() { mgl::vec2f Label::get_size() {
if(!visible)
return {0.0f, 0.0f};
return text.get_bounds().size; return text.get_bounds().size;
} }
} }

View File

@@ -12,6 +12,9 @@ namespace gsr {
List::List(Orientation orientation, Alignment content_alignment) : orientation(orientation), content_alignment(content_alignment) {} List::List(Orientation orientation, Alignment content_alignment) : orientation(orientation), content_alignment(content_alignment) {}
bool List::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f) { bool List::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f) {
if(!visible)
return true;
// We want to store the selected child widget since it can change in the event loop below // We want to store the selected child widget since it can change in the event loop below
Widget *selected_widget = selected_child_widget; Widget *selected_widget = selected_child_widget;
if(selected_widget) { if(selected_widget) {
@@ -55,19 +58,33 @@ namespace gsr {
void List::draw(mgl::Window &window, mgl::vec2f offset) { void List::draw(mgl::Window &window, mgl::vec2f offset) {
update(); update();
if(!visible)
return;
mgl::vec2f draw_pos = position + offset; mgl::vec2f draw_pos = position + offset;
offset = {0.0f, 0.0f}; offset = {0.0f, 0.0f};
Widget *selected_widget = selected_child_widget; Widget *selected_widget = selected_child_widget;
// TODO: Handle start/end alignment // TODO: Handle start/end alignment
const mgl::vec2f size = get_size(); const mgl::vec2f size = get_size();
const mgl::vec2f parent_size = parent_widget ? parent_widget->get_size() : mgl::vec2f(0.0f, 0.0f);
const mgl::vec2f spacing = (spacing_scale * get_theme().window_height).floor(); const mgl::vec2f spacing = (spacing_scale * get_theme().window_height).floor();
switch(orientation) { switch(orientation) {
case Orientation::VERTICAL: { case Orientation::VERTICAL: {
for(auto &widget : widgets) { for(auto &widget : widgets) {
if(content_alignment == Alignment::CENTER) if(!widget->visible)
continue;
// TODO: Do this parent widget alignment for horizontal alignment and for other types of widget alignment
// and other widgets.
// Also take this widget alignment into consideration in get_size.
if(widget->get_horizontal_alignment() == Widget::Alignment::CENTER && parent_size.x > 0.001f)
offset.x = floor(parent_size.x * 0.5f - widget->get_size().x * 0.5f);
else if(content_alignment == Alignment::CENTER)
offset.x = floor(size.x * 0.5f - widget->get_size().x * 0.5f); offset.x = floor(size.x * 0.5f - widget->get_size().x * 0.5f);
else
offset.x = 0.0f;
widget->set_position(draw_pos + offset); widget->set_position(draw_pos + offset);
if(widget.get() != selected_widget) if(widget.get() != selected_widget)
widget->draw(window, mgl::vec2f(0.0f, 0.0f)); widget->draw(window, mgl::vec2f(0.0f, 0.0f));
@@ -77,6 +94,9 @@ namespace gsr {
} }
case Orientation::HORIZONTAL: { case Orientation::HORIZONTAL: {
for(auto &widget : widgets) { for(auto &widget : widgets) {
if(!widget->visible)
continue;
if(content_alignment == Alignment::CENTER) if(content_alignment == Alignment::CENTER)
offset.y = floor(size.y * 0.5f - widget->get_size().y * 0.5f); offset.y = floor(size.y * 0.5f - widget->get_size().y * 0.5f);
widget->set_position(draw_pos + offset); widget->set_position(draw_pos + offset);
@@ -121,11 +141,17 @@ namespace gsr {
// TODO: Cache result // TODO: Cache result
mgl::vec2f List::get_size() { mgl::vec2f List::get_size() {
if(!visible)
return {0.0f, 0.0f};
mgl::vec2f size; mgl::vec2f size;
const mgl::vec2f spacing = (spacing_scale * get_theme().window_height).floor(); const mgl::vec2f spacing = (spacing_scale * get_theme().window_height).floor();
switch(orientation) { switch(orientation) {
case Orientation::VERTICAL: { case Orientation::VERTICAL: {
for(auto &widget : widgets) { for(auto &widget : widgets) {
if(!widget->visible)
continue;
const auto widget_size = widget->get_size(); const auto widget_size = widget->get_size();
size.x = std::max(size.x, widget_size.x); size.x = std::max(size.x, widget_size.x);
size.y += widget_size.y + spacing.y; size.y += widget_size.y + spacing.y;
@@ -134,6 +160,9 @@ namespace gsr {
} }
case Orientation::HORIZONTAL: { case Orientation::HORIZONTAL: {
for(auto &widget : widgets) { for(auto &widget : widgets) {
if(!widget->visible)
continue;
const auto widget_size = widget->get_size(); const auto widget_size = widget->get_size();
size.x += widget_size.x + spacing.x; size.x += widget_size.x + spacing.x;
size.y = std::max(size.y, widget_size.y); size.y = std::max(size.y, widget_size.y);

129
src/gui/RadioButton.cpp Normal file
View File

@@ -0,0 +1,129 @@
#include "../../include/gui/RadioButton.hpp"
#include "../../include/gui/Utils.hpp"
#include "../../include/Theme.hpp"
#include <mglpp/graphics/Rectangle.hpp>
#include <mglpp/window/Window.hpp>
#include <mglpp/window/Event.hpp>
#include <mglpp/system/FloatRect.hpp>
namespace gsr {
static const float padding_top_scale = 0.004629f;
static const float padding_bottom_scale = 0.004629f;
static const float padding_left_scale = 0.007f;
static const float padding_right_scale = 0.007f;
static const float spacing_scale = 0.007f;
static const float border_scale = 0.0015f;
RadioButton::RadioButton(mgl::Font *font) : font(font) {
}
bool RadioButton::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) {
if(!visible)
return true;
const int padding_top = padding_top_scale * get_theme().window_height;
const int padding_bottom = padding_bottom_scale * get_theme().window_height;
const int padding_left = padding_left_scale * get_theme().window_height;
const int padding_right = padding_right_scale * get_theme().window_height;
if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) {
mgl::vec2f draw_pos = position + offset;
for(size_t i = 0; i < items.size(); ++i) {
auto &item = items[i];
const mgl::vec2f item_size = item.text.get_bounds().size + mgl::vec2f(padding_left + padding_right, padding_top + padding_bottom);
const bool mouse_inside = mgl::FloatRect(draw_pos, item_size).contains(mgl::vec2f(event.mouse_button.x, event.mouse_button.y));
if(mouse_inside) {
const size_t prev_selected_item = selected_item;
selected_item = i;
if(selected_item != prev_selected_item && on_selection_changed)
on_selection_changed(item.text.get_string(), item.id);
return false;
}
draw_pos.x += item_size.x + spacing_scale * get_theme().window_height;
}
}
return true;
}
void RadioButton::draw(mgl::Window &window, mgl::vec2f offset) {
if(!visible)
return;
update_if_dirty();
const int padding_top = padding_top_scale * get_theme().window_height;
const int padding_bottom = padding_bottom_scale * get_theme().window_height;
const int padding_left = padding_left_scale * get_theme().window_height;
const int padding_right = padding_right_scale * get_theme().window_height;
const bool can_select_item = !has_parent_with_selected_child_widget();
mgl::vec2f draw_pos = position + offset;
for(size_t i = 0; i < items.size(); ++i) {
auto &item = items[i];
const mgl::vec2f item_size = item.text.get_bounds().size + mgl::vec2f(padding_left + padding_right, padding_top + padding_bottom);
mgl::Rectangle background(item_size.floor());
background.set_position(draw_pos.floor());
background.set_color(i == selected_item ? get_theme().tint_color : mgl::Color(0, 0, 0, 120));
window.draw(background);
const bool mouse_inside = mgl::FloatRect(draw_pos, item_size).contains(window.get_mouse_position().to_vec2f());
if(can_select_item && mouse_inside) {
const int border_size = border_scale * get_theme().window_height;
const mgl::Color border_color = get_theme().tint_color;
draw_rectangle_outline(window, draw_pos.floor(), item_size.floor(), border_color, border_size);
}
item.text.set_position((draw_pos + item_size * 0.5f - item.text.get_bounds().size * 0.5f).floor());
window.draw(item.text);
draw_pos.x += item_size.x + spacing_scale * get_theme().window_height;
}
}
void RadioButton::update_if_dirty() {
if(!dirty)
return;
const int padding_top = padding_top_scale * get_theme().window_height;
const int padding_bottom = padding_bottom_scale * get_theme().window_height;
const int padding_left = padding_left_scale * get_theme().window_height;
const int padding_right = padding_right_scale * get_theme().window_height;
size = { 0.0f, font->get_character_size() + (float)padding_top + (float)padding_bottom };
for(Item &item : items) {
const mgl::vec2f bounds = item.text.get_bounds().size;
size.x += bounds.x + padding_left + padding_right;
}
if(items.size() > 1)
size.x += (items.size() - 1) * spacing_scale * get_theme().window_height;
dirty = false;
}
mgl::vec2f RadioButton::get_size() {
if(!visible)
return {0.0f, 0.0f};
update_if_dirty();
return size;
}
void RadioButton::add_item(const std::string &text, const std::string &id) {
items.push_back({mgl::Text(text, *font), id});
dirty = true;
}
void RadioButton::set_selected_item(const std::string &id) {
for(size_t i = 0; i < items.size(); ++i) {
if(items[i].id == id) {
selected_item = i;
break;
}
}
}
}

View File

@@ -8,8 +8,14 @@ namespace gsr {
ScrollablePage::ScrollablePage(mgl::vec2f size) : size(size) {} ScrollablePage::ScrollablePage(mgl::vec2f size) : size(size) {}
bool ScrollablePage::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) { bool ScrollablePage::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) {
if(!visible)
return true;
const int margin_top = margin_top_scale * get_theme().window_height;
const int margin_left = margin_left_scale * get_theme().window_height;
const mgl::vec2f draw_pos = position + offset; const mgl::vec2f draw_pos = position + offset;
offset = draw_pos + mgl::vec2f(0.0f, get_border_size(window)).floor(); offset = draw_pos + mgl::vec2f(margin_left, get_border_size() + margin_top).floor();
Widget *selected_widget = selected_child_widget; Widget *selected_widget = selected_child_widget;
if(selected_widget) { if(selected_widget) {
@@ -29,29 +35,37 @@ namespace gsr {
} }
void ScrollablePage::draw(mgl::Window &window, mgl::vec2f offset) { void ScrollablePage::draw(mgl::Window &window, mgl::vec2f offset) {
if(!visible)
return;
const int margin_top = margin_top_scale * get_theme().window_height;
const int margin_left = margin_left_scale * get_theme().window_height;
const mgl::vec2f draw_pos = position + offset; const mgl::vec2f draw_pos = position + offset;
offset = draw_pos + mgl::vec2f(0.0f, get_border_size(window)).floor(); offset = draw_pos + mgl::vec2f(margin_left, get_border_size() + margin_top).floor();
Widget *selected_widget = selected_child_widget;
mgl_scissor prev_scissor;
mgl_window_get_scissor(window.internal_window(), &prev_scissor);
mgl_scissor new_scissor = {
mgl_vec2i{(int)draw_pos.x, (int)draw_pos.y},
mgl_vec2i{(int)size.x, (int)size.y}
};
mgl_window_set_scissor(window.internal_window(), &new_scissor);
mgl::Rectangle background(size.floor()); mgl::Rectangle background(size.floor());
background.set_position(draw_pos); background.set_position(draw_pos);
background.set_color(get_theme().scrollable_page_bg_color); background.set_color(get_theme().scrollable_page_bg_color);
window.draw(background); window.draw(background);
mgl::Rectangle border(mgl::vec2f(size.x, get_border_size(window)).floor()); mgl::Rectangle border(mgl::vec2f(size.x, get_border_size()).floor());
border.set_position(draw_pos); border.set_position(draw_pos);
border.set_color(get_theme().tint_color); border.set_color(get_theme().tint_color);
window.draw(border); window.draw(border);
mgl_scissor prev_scissor;
mgl_window_get_scissor(window.internal_window(), &prev_scissor);
const mgl::vec2f content_size = get_size();
mgl_scissor new_scissor = {
mgl_vec2i{(int)offset.x, (int)offset.y},
mgl_vec2i{(int)content_size.x, (int)content_size.y}
};
mgl_window_set_scissor(window.internal_window(), &new_scissor);
Widget *selected_widget = selected_child_widget;
for(auto &widget : widgets) { for(auto &widget : widgets) {
if(widget.get() != selected_widget) if(widget.get() != selected_widget)
widget->draw(window, offset); widget->draw(window, offset);
@@ -63,7 +77,25 @@ namespace gsr {
mgl_window_set_scissor(window.internal_window(), &prev_scissor); mgl_window_set_scissor(window.internal_window(), &prev_scissor);
} }
float ScrollablePage::get_border_size(mgl::Window &window) const { mgl::vec2f ScrollablePage::get_size() {
return window.get_size().y * 0.004f; if(!visible)
return {0.0f, 0.0f};
const int margin_top = margin_top_scale * get_theme().window_height;
const int margin_bottom = margin_bottom_scale * get_theme().window_height;
const int margin_left = margin_left_scale * get_theme().window_height;
const int margin_right = margin_right_scale * get_theme().window_height;
return size - mgl::vec2f(margin_left + margin_right, margin_top + margin_bottom + get_border_size());
}
void ScrollablePage::set_margins(float top, float bottom, float left, float right) {
margin_top_scale = top;
margin_bottom_scale = bottom;
margin_left_scale = left;
margin_right_scale = right;
}
float ScrollablePage::get_border_size() const {
return 0.004f * get_theme().window_height;
} }
} }

View File

@@ -6,6 +6,9 @@ namespace gsr {
StaticPage::StaticPage(mgl::vec2f size) : size(size) {} StaticPage::StaticPage(mgl::vec2f size) : size(size) {}
bool StaticPage::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) { bool StaticPage::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) {
if(!visible)
return true;
const mgl::vec2f draw_pos = position + offset; const mgl::vec2f draw_pos = position + offset;
offset = draw_pos; offset = draw_pos;
Widget *selected_widget = selected_child_widget; Widget *selected_widget = selected_child_widget;
@@ -27,6 +30,9 @@ namespace gsr {
} }
void StaticPage::draw(mgl::Window &window, mgl::vec2f offset) { void StaticPage::draw(mgl::Window &window, mgl::vec2f offset) {
if(!visible)
return;
const mgl::vec2f draw_pos = position + offset; const mgl::vec2f draw_pos = position + offset;
offset = draw_pos; offset = draw_pos;
Widget *selected_widget = selected_child_widget; Widget *selected_widget = selected_child_widget;
@@ -50,4 +56,11 @@ namespace gsr {
mgl_window_set_scissor(window.internal_window(), &prev_scissor); mgl_window_set_scissor(window.internal_window(), &prev_scissor);
} }
mgl::vec2f StaticPage::get_size() {
if(!visible)
return {0.0f, 0.0f};
return size;
}
} }

View File

@@ -32,4 +32,34 @@ namespace gsr {
parent_widget->remove_widget_as_selected_in_parent(); parent_widget->remove_widget_as_selected_in_parent();
} }
} }
bool Widget::has_parent_with_selected_child_widget() const {
// TODO: Optimize since this is called in draw function in widgets
if(parent_widget) {
if(parent_widget->selected_child_widget)
return true;
return parent_widget->has_parent_with_selected_child_widget();
}
return false;
}
void Widget::set_horizontal_alignment(Alignment alignment) {
horizontal_aligment = alignment;
}
void Widget::set_vertical_alignment(Alignment alignment) {
vertical_aligment = alignment;
}
Widget::Alignment Widget::get_horizontal_alignment() const {
return horizontal_aligment;
}
Widget::Alignment Widget::get_vertical_alignment() const {
return vertical_aligment;
}
void Widget::set_visible(bool visible) {
this->visible = visible;
}
} }

View File

@@ -3,6 +3,7 @@
#include "../include/gui/ScrollablePage.hpp" #include "../include/gui/ScrollablePage.hpp"
#include "../include/gui/DropdownButton.hpp" #include "../include/gui/DropdownButton.hpp"
#include "../include/gui/Button.hpp" #include "../include/gui/Button.hpp"
#include "../include/gui/RadioButton.hpp"
#include "../include/gui/Entry.hpp" #include "../include/gui/Entry.hpp"
#include "../include/gui/CheckBox.hpp" #include "../include/gui/CheckBox.hpp"
#include "../include/gui/ComboBox.hpp" #include "../include/gui/ComboBox.hpp"
@@ -214,8 +215,18 @@ static void add_widgets_to_settings_page(mgl::vec2i window_size, mgl::vec2f sett
settings_page->add_widget(std::move(back_button)); settings_page->add_widget(std::move(back_button));
auto settings_list = std::make_unique<gsr::List>(gsr::List::Orientation::VERTICAL); auto settings_list = std::make_unique<gsr::List>(gsr::List::Orientation::VERTICAL);
settings_list->set_position(mgl::vec2f(0.02f * gsr::get_theme().window_height, 0.02f * gsr::get_theme().window_height).floor());
{ {
auto view_radio_button = std::make_unique<gsr::RadioButton>(&gsr::get_theme().body_font);
view_radio_button->add_item("Simple view", "simple");
view_radio_button->add_item("Advanced view", "advanced");
view_radio_button->set_horizontal_alignment(gsr::Widget::Alignment::CENTER);
gsr::RadioButton *view_radio_button_ptr = view_radio_button.get();
settings_list->add_widget(std::move(view_radio_button));
gsr::Widget *color_range_list_ptr = nullptr;
gsr::Widget *codec_list_ptr = nullptr;
gsr::Widget *framerate_mode_list_ptr = nullptr;
auto record_area_list = std::make_unique<gsr::List>(gsr::List::Orientation::VERTICAL); auto record_area_list = std::make_unique<gsr::List>(gsr::List::Orientation::VERTICAL);
{ {
record_area_list->add_widget(std::make_unique<gsr::Label>(&gsr::get_theme().body_font, "Record area:", gsr::get_theme().text_color)); record_area_list->add_widget(std::make_unique<gsr::Label>(&gsr::get_theme().body_font, "Record area:", gsr::get_theme().text_color));
@@ -302,6 +313,7 @@ static void add_widgets_to_settings_page(mgl::vec2i window_size, mgl::vec2f sett
color_range_box->add_item("Full", "full"); color_range_box->add_item("Full", "full");
color_range_list->add_widget(std::move(color_range_box)); color_range_list->add_widget(std::move(color_range_box));
} }
color_range_list_ptr = color_range_list.get();
quality_list->add_widget(std::move(color_range_list)); quality_list->add_widget(std::move(color_range_list));
} }
settings_list->add_widget(std::move(quality_list)); settings_list->add_widget(std::move(quality_list));
@@ -341,6 +353,7 @@ static void add_widgets_to_settings_page(mgl::vec2i window_size, mgl::vec2f sett
} }
codec_list->add_widget(std::move(audio_codec_list)); codec_list->add_widget(std::move(audio_codec_list));
} }
codec_list_ptr = codec_list.get();
settings_list->add_widget(std::move(codec_list)); settings_list->add_widget(std::move(codec_list));
auto framerate_info_list = std::make_unique<gsr::List>(gsr::List::Orientation::HORIZONTAL); auto framerate_info_list = std::make_unique<gsr::List>(gsr::List::Orientation::HORIZONTAL);
@@ -363,6 +376,7 @@ static void add_widgets_to_settings_page(mgl::vec2i window_size, mgl::vec2f sett
framerate_mode_box->add_item("Variable", "vfr"); framerate_mode_box->add_item("Variable", "vfr");
framerate_mode_list->add_widget(std::move(framerate_mode_box)); framerate_mode_list->add_widget(std::move(framerate_mode_box));
} }
framerate_mode_list_ptr = framerate_mode_list.get();
framerate_info_list->add_widget(std::move(framerate_mode_list)); framerate_info_list->add_widget(std::move(framerate_mode_list));
} }
settings_list->add_widget(std::move(framerate_info_list)); settings_list->add_widget(std::move(framerate_info_list));
@@ -397,6 +411,16 @@ static void add_widgets_to_settings_page(mgl::vec2i window_size, mgl::vec2f sett
settings_list->add_widget(std::make_unique<gsr::CheckBox>(&gsr::get_theme().body_font, "Show recording started notification")); settings_list->add_widget(std::make_unique<gsr::CheckBox>(&gsr::get_theme().body_font, "Show recording started notification"));
//settings_list->add_widget(std::make_unique<gsr::CheckBox>(&gsr::get_theme().body_font, "Show recording stopped notification")); //settings_list->add_widget(std::make_unique<gsr::CheckBox>(&gsr::get_theme().body_font, "Show recording stopped notification"));
settings_list->add_widget(std::make_unique<gsr::CheckBox>(&gsr::get_theme().body_font, "Show video saved notification")); settings_list->add_widget(std::make_unique<gsr::CheckBox>(&gsr::get_theme().body_font, "Show video saved notification"));
view_radio_button_ptr->on_selection_changed = [=](const std::string &text, const std::string &id) {
(void)text;
const bool advanced_view = id == "advanced";
color_range_list_ptr->set_visible(advanced_view);
codec_list_ptr->set_visible(advanced_view);
framerate_mode_list_ptr->set_visible(advanced_view);
};
view_radio_button_ptr->on_selection_changed("Simple", "simple");
} }
settings_content_page->add_widget(std::move(settings_list)); settings_content_page->add_widget(std::move(settings_list));
} }
@@ -515,18 +539,22 @@ int main(int argc, char **argv) {
const mgl::vec2f settings_page_size(window_size.x * 0.3333f, window_size.y * 0.7f); const mgl::vec2f settings_page_size(window_size.x * 0.3333f, window_size.y * 0.7f);
const mgl::vec2f settings_page_position = (window_size.to_vec2f() * 0.5f - settings_page_size * 0.5f).floor(); const mgl::vec2f settings_page_position = (window_size.to_vec2f() * 0.5f - settings_page_size * 0.5f).floor();
const float settings_body_margin = 0.02f;
auto replay_settings_content = std::make_unique<gsr::ScrollablePage>(settings_page_size); auto replay_settings_content = std::make_unique<gsr::ScrollablePage>(settings_page_size);
gsr::ScrollablePage *replay_settings_content_ptr = replay_settings_content.get(); gsr::ScrollablePage *replay_settings_content_ptr = replay_settings_content.get();
replay_settings_content->set_position(settings_page_position); replay_settings_content->set_position(settings_page_position);
replay_settings_content->set_margins(settings_body_margin, settings_body_margin, settings_body_margin, settings_body_margin);
auto record_settings_content = std::make_unique<gsr::ScrollablePage>(settings_page_size); auto record_settings_content = std::make_unique<gsr::ScrollablePage>(settings_page_size);
gsr::ScrollablePage *record_settings_content_ptr = record_settings_content.get(); gsr::ScrollablePage *record_settings_content_ptr = record_settings_content.get();
record_settings_content->set_position(settings_page_position); record_settings_content->set_position(settings_page_position);
record_settings_content->set_margins(settings_body_margin, settings_body_margin, settings_body_margin, settings_body_margin);
auto stream_settings_content = std::make_unique<gsr::ScrollablePage>(settings_page_size); auto stream_settings_content = std::make_unique<gsr::ScrollablePage>(settings_page_size);
gsr::ScrollablePage *stream_settings_content_ptr = stream_settings_content.get(); gsr::ScrollablePage *stream_settings_content_ptr = stream_settings_content.get();
stream_settings_content->set_position(settings_page_position); stream_settings_content->set_position(settings_page_position);
stream_settings_content->set_margins(settings_body_margin, settings_body_margin, settings_body_margin, settings_body_margin);
gsr::StaticPage replay_settings_page(window_size.to_vec2f()); gsr::StaticPage replay_settings_page(window_size.to_vec2f());
replay_settings_page.add_widget(std::move(replay_settings_content)); replay_settings_page.add_widget(std::move(replay_settings_content));