aboutsummaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/Button.cpp11
-rw-r--r--src/gui/CheckBox.cpp11
-rw-r--r--src/gui/ComboBox.cpp11
-rw-r--r--src/gui/DropdownButton.cpp9
-rw-r--r--src/gui/Entry.cpp9
-rw-r--r--src/gui/Label.cpp6
-rw-r--r--src/gui/List.cpp31
-rw-r--r--src/gui/RadioButton.cpp129
-rw-r--r--src/gui/ScrollablePage.cpp60
-rw-r--r--src/gui/StaticPage.cpp13
-rw-r--r--src/gui/Widget.cpp30
11 files changed, 302 insertions, 18 deletions
diff --git a/src/gui/Button.cpp b/src/gui/Button.cpp
index 1849737..d2c4a2c 100644
--- a/src/gui/Button.cpp
+++ b/src/gui/Button.cpp
@@ -17,6 +17,9 @@ namespace gsr {
}
bool Button::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) {
+ if(!visible)
+ return true;
+
const mgl::vec2f item_size = get_size().floor();
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 });
@@ -27,6 +30,9 @@ namespace gsr {
}
void Button::draw(mgl::Window &window, mgl::vec2f offset) {
+ if(!visible)
+ return;
+
const mgl::vec2f draw_pos = position + offset;
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());
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)
draw_rectangle_outline(window, draw_pos, item_size, get_theme().tint_color, border_scale * get_theme().window_height);
}
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_bottom = padding_bottom_scale * get_theme().window_height;
const int padding_left = padding_left_scale * get_theme().window_height;
diff --git a/src/gui/CheckBox.cpp b/src/gui/CheckBox.cpp
index b337d5f..94e5f9e 100644
--- a/src/gui/CheckBox.cpp
+++ b/src/gui/CheckBox.cpp
@@ -16,6 +16,9 @@ namespace gsr {
}
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) {
const bool clicked_inside = mgl::FloatRect(position + offset, get_size()).contains({ (float)event.mouse_button.x, (float)event.mouse_button.y });
if(clicked_inside)
@@ -25,6 +28,9 @@ namespace gsr {
}
void CheckBox::draw(mgl::Window &window, mgl::vec2f offset) {
+ if(!visible)
+ return;
+
const mgl::vec2f draw_pos = position + offset;
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());
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) {
const int border_size = std::max(1.0f, border_scale * get_theme().window_height);
const mgl::Color border_color = get_theme().tint_color;
@@ -54,6 +60,9 @@ namespace gsr {
}
mgl::vec2f CheckBox::get_size() {
+ if(!visible)
+ return {0.0f, 0.0f};
+
mgl::vec2f size = text.get_bounds().size;
const mgl::vec2f checkbox_size = get_checkbox_size();
size.x += checkbox_size.x + spacing_scale * get_theme().window_height;
diff --git a/src/gui/ComboBox.cpp b/src/gui/ComboBox.cpp
index 9decab7..0b232f2 100644
--- a/src/gui/ComboBox.cpp
+++ b/src/gui/ComboBox.cpp
@@ -19,6 +19,9 @@ namespace gsr {
}
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_bottom = padding_bottom_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) {
+ if(!visible)
+ return;
+
update_if_dirty();
if(items.empty())
@@ -92,7 +98,7 @@ namespace gsr {
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);
Item &item = items[selected_item];
@@ -164,6 +170,9 @@ namespace gsr {
}
mgl::vec2f ComboBox::get_size() {
+ if(!visible)
+ return {0.0f, 0.0f};
+
update_if_dirty();
const int padding_top = padding_top_scale * get_theme().window_height;
diff --git a/src/gui/DropdownButton.cpp b/src/gui/DropdownButton.cpp
index 5cc9a3d..50a3105 100644
--- a/src/gui/DropdownButton.cpp
+++ b/src/gui/DropdownButton.cpp
@@ -26,6 +26,9 @@ namespace gsr {
}
bool DropdownButton::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) {
+ if(!visible)
+ return true;
+
if(event.type == mgl::Event::MouseMoved) {
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
@@ -56,6 +59,9 @@ namespace gsr {
}
void DropdownButton::draw(mgl::Window &window, mgl::vec2f offset) {
+ if(!visible)
+ return;
+
update_if_dirty();
const mgl::vec2f draw_pos = position + offset;
@@ -199,6 +205,9 @@ namespace gsr {
}
mgl::vec2f DropdownButton::get_size() {
+ if(!visible)
+ return {0.0f, 0.0f};
+
update_if_dirty();
return size;
}
diff --git a/src/gui/Entry.cpp b/src/gui/Entry.cpp
index e853bc8..e74e41e 100644
--- a/src/gui/Entry.cpp
+++ b/src/gui/Entry.cpp
@@ -22,6 +22,9 @@ namespace gsr {
}
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) {
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) {
@@ -40,6 +43,9 @@ namespace gsr {
}
void Entry::draw(mgl::Window &window, mgl::vec2f offset) {
+ if(!visible)
+ return;
+
const mgl::vec2f draw_pos = position + offset;
const int padding_top = padding_top_scale * get_theme().window_height;
@@ -66,6 +72,9 @@ namespace gsr {
}
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_bottom = padding_bottom_scale * get_theme().window_height;
return { max_width, text.get_bounds().size.y + padding_top + padding_bottom };
diff --git a/src/gui/Label.cpp b/src/gui/Label.cpp
index 08f720b..09fd1a6 100644
--- a/src/gui/Label.cpp
+++ b/src/gui/Label.cpp
@@ -11,11 +11,17 @@ namespace gsr {
}
void Label::draw(mgl::Window &window, mgl::vec2f offset) {
+ if(!visible)
+ return;
+
text.set_position((position + offset).floor());
window.draw(text);
}
mgl::vec2f Label::get_size() {
+ if(!visible)
+ return {0.0f, 0.0f};
+
return text.get_bounds().size;
}
} \ No newline at end of file
diff --git a/src/gui/List.cpp b/src/gui/List.cpp
index e8c02e0..0c5f78e 100644
--- a/src/gui/List.cpp
+++ b/src/gui/List.cpp
@@ -12,6 +12,9 @@ namespace gsr {
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) {
+ if(!visible)
+ return true;
+
// We want to store the selected child widget since it can change in the event loop below
Widget *selected_widget = selected_child_widget;
if(selected_widget) {
@@ -55,19 +58,33 @@ namespace gsr {
void List::draw(mgl::Window &window, mgl::vec2f offset) {
update();
+ if(!visible)
+ return;
+
mgl::vec2f draw_pos = position + offset;
offset = {0.0f, 0.0f};
Widget *selected_widget = selected_child_widget;
// TODO: Handle start/end alignment
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();
switch(orientation) {
case Orientation::VERTICAL: {
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);
+ else
+ offset.x = 0.0f;
widget->set_position(draw_pos + offset);
if(widget.get() != selected_widget)
widget->draw(window, mgl::vec2f(0.0f, 0.0f));
@@ -77,6 +94,9 @@ namespace gsr {
}
case Orientation::HORIZONTAL: {
for(auto &widget : widgets) {
+ if(!widget->visible)
+ continue;
+
if(content_alignment == Alignment::CENTER)
offset.y = floor(size.y * 0.5f - widget->get_size().y * 0.5f);
widget->set_position(draw_pos + offset);
@@ -121,11 +141,17 @@ namespace gsr {
// TODO: Cache result
mgl::vec2f List::get_size() {
+ if(!visible)
+ return {0.0f, 0.0f};
+
mgl::vec2f size;
const mgl::vec2f spacing = (spacing_scale * get_theme().window_height).floor();
switch(orientation) {
case Orientation::VERTICAL: {
for(auto &widget : widgets) {
+ if(!widget->visible)
+ continue;
+
const auto widget_size = widget->get_size();
size.x = std::max(size.x, widget_size.x);
size.y += widget_size.y + spacing.y;
@@ -134,6 +160,9 @@ namespace gsr {
}
case Orientation::HORIZONTAL: {
for(auto &widget : widgets) {
+ if(!widget->visible)
+ continue;
+
const auto widget_size = widget->get_size();
size.x += widget_size.x + spacing.x;
size.y = std::max(size.y, widget_size.y);
diff --git a/src/gui/RadioButton.cpp b/src/gui/RadioButton.cpp
new file mode 100644
index 0000000..e5ba02a
--- /dev/null
+++ b/src/gui/RadioButton.cpp
@@ -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;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/gui/ScrollablePage.cpp b/src/gui/ScrollablePage.cpp
index 73b445e..2c1143a 100644
--- a/src/gui/ScrollablePage.cpp
+++ b/src/gui/ScrollablePage.cpp
@@ -8,8 +8,14 @@ namespace gsr {
ScrollablePage::ScrollablePage(mgl::vec2f size) : size(size) {}
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;
- 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;
if(selected_widget) {
@@ -29,29 +35,37 @@ namespace gsr {
}
void ScrollablePage::draw(mgl::Window &window, mgl::vec2f offset) {
- const mgl::vec2f draw_pos = position + offset;
- offset = draw_pos + mgl::vec2f(0.0f, get_border_size(window)).floor();
- Widget *selected_widget = selected_child_widget;
+ if(!visible)
+ return;
- mgl_scissor prev_scissor;
- mgl_window_get_scissor(window.internal_window(), &prev_scissor);
+ const int margin_top = margin_top_scale * get_theme().window_height;
+ const int margin_left = margin_left_scale * get_theme().window_height;
- 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);
+ const mgl::vec2f draw_pos = position + offset;
+ offset = draw_pos + mgl::vec2f(margin_left, get_border_size() + margin_top).floor();
mgl::Rectangle background(size.floor());
background.set_position(draw_pos);
background.set_color(get_theme().scrollable_page_bg_color);
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_color(get_theme().tint_color);
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) {
if(widget.get() != selected_widget)
widget->draw(window, offset);
@@ -63,7 +77,25 @@ namespace gsr {
mgl_window_set_scissor(window.internal_window(), &prev_scissor);
}
- float ScrollablePage::get_border_size(mgl::Window &window) const {
- return window.get_size().y * 0.004f;
+ mgl::vec2f ScrollablePage::get_size() {
+ 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;
}
} \ No newline at end of file
diff --git a/src/gui/StaticPage.cpp b/src/gui/StaticPage.cpp
index a6d9b64..73adfa0 100644
--- a/src/gui/StaticPage.cpp
+++ b/src/gui/StaticPage.cpp
@@ -6,6 +6,9 @@ namespace gsr {
StaticPage::StaticPage(mgl::vec2f size) : size(size) {}
bool StaticPage::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) {
+ if(!visible)
+ return true;
+
const mgl::vec2f draw_pos = position + offset;
offset = draw_pos;
Widget *selected_widget = selected_child_widget;
@@ -27,6 +30,9 @@ namespace gsr {
}
void StaticPage::draw(mgl::Window &window, mgl::vec2f offset) {
+ if(!visible)
+ return;
+
const mgl::vec2f draw_pos = position + offset;
offset = draw_pos;
Widget *selected_widget = selected_child_widget;
@@ -50,4 +56,11 @@ namespace gsr {
mgl_window_set_scissor(window.internal_window(), &prev_scissor);
}
+
+ mgl::vec2f StaticPage::get_size() {
+ if(!visible)
+ return {0.0f, 0.0f};
+
+ return size;
+ }
} \ No newline at end of file
diff --git a/src/gui/Widget.cpp b/src/gui/Widget.cpp
index 4baeff1..8732bd7 100644
--- a/src/gui/Widget.cpp
+++ b/src/gui/Widget.cpp
@@ -32,4 +32,34 @@ namespace gsr {
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;
+ }
} \ No newline at end of file