Skip to content
This repository has been archived by the owner on Aug 29, 2024. It is now read-only.

Commit

Permalink
Close #322: getWidget -> template<W = Widget>
Browse files Browse the repository at this point in the history
  • Loading branch information
xparq committed Aug 1, 2023
1 parent 848cbf6 commit 9b46834
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 70 deletions.
12 changes: 10 additions & 2 deletions include/sfw/GUI-main.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ class GUI: public VBox
// Convert screen pixel coordinates to window position (See the sf::Mouse docs!)
sf::Vector2f convertMousePosition(int x, int y) const;

// ---- Callbacks ---------------------------------------------------------
// ---- Callbacks ------------------------------------------------------------
void draw(const gfx::RenderContext& ctx) const override;

// If the GUI doesn't manage the entire window, and its size hasn't been
Expand All @@ -155,14 +155,15 @@ class GUI: public VBox
// for all the other widgets)
void onThemeChanged() override;

// ---- Helpers -----------------------------------------------------------
// ---- Helpers --------------------------------------------------------------

// Fill the window if managing (owning) it, else just the GUI rect,
// with the current Theme::bgColor, then show the wallpaper, if set
// Note: a clearing background fill is needed even when there's a
// wallpaper, because the image may be translucent!
void renderBackground();

// ---- Data -----------------------------------------------------------------
std::error_code m_error;
sf::RenderWindow& m_window;
bool m_own_window;
Expand All @@ -173,6 +174,13 @@ class GUI: public VBox
sf::Time m_sessionTime;
std::unordered_map<std::string, Widget*> widgets;
bool m_closed = false;

// ---- Misc. hackery... -----------------------------------------------------
// Convenience helper to find the GUI instance easily (assuming the client
// app only has one). Init'ed/updated by the GUI::ctor.
static GUI* DefaultInstance;
// And allow (only) the Widget class to actually use it:
friend class Widget;
};

} // namespace
Expand Down
93 changes: 72 additions & 21 deletions include/sfw/Widget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,35 +84,25 @@ class Widget : public gfx::Drawable, public Event::Handler

bool focused() const;

// Set/Reset the internal name of the widget
// Set/Reset widget name
//
// Notes:
// - If a widget is not assigned a name explicitly, it will have a unique
// default ID (e.g. the hex. representation of its address).
// default ID (e.g. the hex. representation of its (original) mem. address).
// (Note: while widgets are not copied/moved after being added to the GUI,
// they can be during setup.)
//
// - If the name has already been assigned to another widget, it will lose
// its explicit name (reverting to the default), and the new widget will
// take over, having that name thereafter.
// - If the name has already been assigned to another widget, the newly named
// widget will win, stealing the name from the other widget, which will then
// revert to having a default ID instead.
// (Perhaps unsurprisingly, this behavior might change in the future. ;) )
void setName(const std::string&);

// Get the internal name of a widget (or its default ID if hasn't been named)
// If a widget is specified, that one will be looked up, instead of the current one.
//
// Note: This may be a slow operation, intended mainly for diagnostic use!
// Get the optional name of a widget (or its internal ID, if unnamed)
// If a widget is specified then that one will be looked up, not the current one.
// Note: This may be a slow operation (intended mainly for diagnostics).
std::string getName(Widget* widget = nullptr) const;

// Find a registered widget by name
// Returns nullptr if the name was not found.
Widget* getWidget(const std::string& name) const;

// Method-chaining helper for setting up nested widget structures
// (Note: getParent is not public, plus this may get arguments in the future.)
WidgetContainer* parent(/*!!enum FailMode mode = or_burn!!*/) { return getParent(); }
WidgetContainer* parent(/*!!enum FailMode mode = or_burn!!*/) const { return getParent(); }

// Similarly (!!but it might finally be hidden/removed, if the above goes live):
const sf::Transform& getTransform() const;

Widget* setTooltip(const std::string& text);

protected:
Expand Down Expand Up @@ -221,8 +211,69 @@ friend class Tooltip; // just to access getMain() via Tooltip::m_owner->!
void draw_outline(const gfx::RenderContext& ctx, sf::Color outlinecolor = sf::Color::Red,
sf::Color fillcolor = sf::Color::Transparent) const; // Well, with fillColor, now it can also do interior, not just "outline"
#endif

// ---- Misc. hackery... -----------------------------------------------------

// Rotation, scale and translation adjustments for the widget-local
// gfx. context, relative to the parent's
//!!Shouldn't really be public, or even exist, if we had a proper render-context API already...
public:
const sf::Transform& getTransform() const;

// Internal helper to shield this header from some extra dependencies
// See the freestanding functions that need this, below the class!
//
// Note: the only reason this is a (public static) member, not a free
// fn. itself is because it needs access to protected/private stuff
// in both the Widget and the GUI classes, and this way the (brittle)
// friend declarations can be minimized.
// Alas, it needs to be public in exchange, so those free template functions
// can use it... (Well, at least it's hidden at the bottom of a class;
// kinda considered private enough then, right?...)
//
public:
static Widget* getWidget_proxy(const std::string& name, const Widget* w = nullptr);
// If w == null, try the global default (singleton) GUI manager.
// If w is set, use that to look up its actual manager object.

}; // class Widget


//----------------------------------------------------------------------------
// Misc...
//----------------------------------------------------------------------------
// Find widget by name
// Returns nullptr if the name was not found.
template <class W = Widget> W* getWidget(const std::string& name, const Widget* w) { return (W*)Widget::getWidget_proxy(name, w); }
template <class W = Widget> W* getWidget(const std::string& name, const Widget& w) { return (W*)Widget::getWidget_proxy(name, &w); }
// Plus this one for convenience: the last created GUI instance will register itself as the default
// (singleton) widget manager, so we can talk to it, provided we know for sure there can't be
// another one in our app.
template <class W = Widget> W* getWidget(const std::string& name) { return (W*)Widget::getWidget_proxy(name); }
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!
!! Why aren't these above just Widget:: (or GUI::) members?
!! Well, this remnant, removed from the class (as (part) of #322) may help
!! illuminate it:
!!
Widget* getWidget(const std::string& name) const;
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//!!
//!! JESUS CHRIST, C++, WTF?! :-ooo Get your shit together! ;) See #322...
//!! Not even this sad workaround can work (except in MSVC, amen to that...)!
//!! See the freestanding getWidget() functions after this class for the current
//!! alternative solution.
//!!
//!! template <class W = Widget>
//!! W* getWidget(const std::string& name) const { return (W*)getWidget_internal(name); }
//!! //! Would be nice to have it templated directly, but that would introduce
//!! //! a nasty cross-dependency on the top-level GUI stuff.
//!! Widget* getWidget_internal(const std::string& name) const;
//!!
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/

} // namespace

#endif // GUI_WIDGET_HPP
32 changes: 16 additions & 16 deletions src/examples/demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ int main()
auto buttfactory = buttons_vbox->add(new HBox);
auto labeller = buttfactory->add(sfw::TextBox(100))->set("Edit Me!")->setPlaceholder("Button label")
->setCallback([&] {
if (auto b = (sfw::Button*)demo.getWidget("button spawner"); b) b->click();
if (auto b = sfw::getWidget<sfw::Button>("button spawner", demo); b) b->click();
});
buttfactory->add(sfw::Button("Create button", [&] {
buttons_vbox->addAfter(buttfactory, new sfw::Button(labeller->get()));
Expand All @@ -273,7 +273,7 @@ int main()

auto utf8button_tag = "UTF-8 by default:"; // We'll also use this text to find the button.
buttons_form->add(utf8button_tag, sfw::Button("Ψ ≠ 99° ± β")); // Note: this source is already UTF-8 encoded!
cout << "UTF-8 button text got back as: \"" << ((sfw::Button*)demo.getWidget(utf8button_tag))->getText() << "\"\n";
cout << "UTF-8 button text got back as: \"" << sfw::getWidget<sfw::Button>(utf8button_tag, demo)->getText() << "\"\n";

// Bitmap buttons
auto imgbuttons_form = left_panel->add(sfw::Form());
Expand Down Expand Up @@ -325,9 +325,9 @@ int main()
// (`jumpy_thumb_click = true` allows immediately readjusting the pos.
// of the slider thumb on clicking it)
auto cropslider = (new sfw::Slider({.jumpy_thumb_click = true}, 100))->setCallback([&](auto* w) {
((sfw::ProgressBar*)w->getWidget("cropbar"))->set(w->get());
sfw::getWidget<sfw::ProgressBar>("cropbar")->set(w->get());
// Show the slider value in a text box retrieved by its name:
auto tbox = (sfw::TextBox*)w->getWidget("crop%");
auto tbox = sfw::getWidget<sfw::TextBox>("crop%");
if (!tbox) cerr << "Named TextBox not found! :-o\n";
else tbox->set(w->get() ? to_string((int)w->get()) : "");
imgCrop->setCropRect({{(int)(w->get() / 4), (int)(w->get() / 10)},
Expand Down Expand Up @@ -357,14 +357,14 @@ int main()
if (demo.setTheme(themecfg))
{
// Update the wallpaper controls:
if (auto wptoggle = (sfw::CheckBox*)demo.getWidget("Wallpaper"); wptoggle)
if (auto wptoggle = sfw::getWidget<sfw::CheckBox>("Wallpaper"); wptoggle)
wptoggle->set(demo.hasWallpaper()) // set() first, as it returns CheckBox*
->enable(demo.hasWallpaper()); // (enable() would "degrade" to Widget*)
if (auto wpalpha = demo.getWidget("Wallpaper α"); wpalpha) // auto -> Widget*
->enable(demo.hasWallpaper()); // (enable() would degrade to Widget*)
if (auto wpalpha = sfw::getWidget("Wallpaper α"); wpalpha) // auto -> Widget*
wpalpha->enable(demo.hasWallpaper());

// Update the bg. color selector's "Default" value:
if (auto bgsel = (OBColor*)demo.getWidget("Bg. color"); bgsel)
if (auto bgsel = sfw::getWidget<OBColor>("Bg. color"); bgsel)
bgsel->assign("Default", themecfg.bgColor);

//!! Update the font size slider:
Expand All @@ -385,11 +385,11 @@ int main()
right_bar->add(sfw::Slider({.range = {8, 18}}, 100), "font size slider")
//!! Would be tempting to sync the initial font size directly with
//!! the theme selector widget -- but can't: the GUI is not up yet, so:
//!! ->set(((OBTheme*)(w->getWidget("theme-selector")))->currentRef().textSize) //! This would fail here!
//!! ->set(w->getWidget<OBTheme>("theme-selector")->currentRef().textSize) //! This would fail here!
->set((float)themes[DEFAULT_THEME].textSize)
->setCallback([&] (auto* w){
assert(w->getWidget("theme-selector"));
auto& themecfg = ((OBTheme*)(w->getWidget("theme-selector")))->currentRef();
assert(sfw::getWidget("theme-selector", w));
auto& themecfg = sfw::getWidget<OBTheme>("theme-selector", w)->currentRef();
themecfg.textSize = (size_t)w->get();
cerr << "font size: "<< themecfg.textSize << endl; //!!#196
demo.setTheme(themecfg);
Expand Down Expand Up @@ -422,8 +422,8 @@ cerr << "font size: "<< themecfg.textSize << endl; //!!#196
bgform->add("Wallpaper α", sfw::Slider({.range={0, 255}}, 75))
->set(demo.getWallpaper().getColor().a)
->setCallback([&](auto* w) {
assert(w->getWidget("theme-selector"));
auto& themecfg = ((OBTheme*)(w->getWidget("theme-selector")))->currentRef();
assert(sfw::getWidget("theme-selector", w));
auto& themecfg = sfw::getWidget<OBTheme>("theme-selector", w)->currentRef();
themecfg.wallpaper.tint = {themecfg.wallpaper.tint.r,
themecfg.wallpaper.tint.g,
themecfg.wallpaper.tint.b,
Expand Down Expand Up @@ -455,8 +455,8 @@ cerr << "font size: "<< themecfg.textSize << endl; //!!#196
// (after setup, as these may trigger callbacks etc.)
//

// Change the font size (to bigger?) to "prime" SFML to avoid #196! :-o
if (auto w = (sfw::Slider*)demo.getWidget("font size slider"); w)
//!! Change the font size (to bigger?) to "prime" SFML to avoid #196! :-o
if (auto w = sfw::getWidget<sfw::Slider>("font size slider", demo); w)
w->update(14);

sliderForRotation->set(104);
Expand Down Expand Up @@ -500,7 +500,7 @@ cerr << "font size: "<< themecfg.textSize << endl; //!!#196
//----------------------------------------------------------------------------
void background_thread_main(sfw::GUI& gui)
{
auto rot_slider = (sfw::Slider*)gui.getWidget("rotation-slider");
auto rot_slider = sfw::getWidget<sfw::Slider>("rotation-slider", gui);
if (!rot_slider)
return;

Expand Down
10 changes: 10 additions & 0 deletions src/lib/GUI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ using namespace std;
namespace sfw
{

// See the header for more on this:
GUI* GUI::DefaultInstance = nullptr;


//----------------------------------------------------------------------------
GUI::GUI(sf::RenderWindow& window, const sfw::Theme::Cfg& themeCfg, bool own_the_window):
m_error(), // no error by default
Expand All @@ -29,6 +33,12 @@ GUI::GUI(sf::RenderWindow& window, const sfw::Theme::Cfg& themeCfg, bool own_the
// Also register ourselves to our own widget registry:
widgets["/"] = this;

// Add as the "most recent" GUI manager:
if (GUI::DefaultInstance) {
cerr << "- Note: overriding previous default GUI instance (" << DefaultInstance << " with newly created " << this << ")\n";
}
GUI::DefaultInstance = this;

reset();
}

Expand Down
37 changes: 27 additions & 10 deletions src/lib/Widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,33 @@
namespace sfw
{

//----------------------------------------------------------------------------
// See the header...
/*static*/ Widget* Widget::getWidget_proxy(const std::string& name, const Widget* w/* = nullptr*/)
{
assert(GUI::DefaultInstance);
auto Main = w ? w->getMain() : GUI::DefaultInstance;

assert(Main);
return Main->recall(name);
}

/*!!Obsolete since #322:
Widget* Widget::getWidget(const std::string& name) const
{
//!!Log if dangling or 'name' not found!
if (GUI* Main = getMain(); Main)
{
return Main->recall(name);
}
return nullptr;
}
!!*/



//----------------------------------------------------------------------------
Widget::Widget() :
m_focusable(true),
m_state(WidgetState::Default),
Expand Down Expand Up @@ -121,16 +148,6 @@ std::string Widget::getName(Widget* widget) const
}


Widget* Widget::getWidget(const std::string& name) const
{
if (GUI* Main = getMain(); Main)
{
return Main->recall(name);
}
return nullptr;
}


//----------------------------------------------------------------------------
Widget* Widget::setPosition(const sf::Vector2f& pos)
{
Expand Down
8 changes: 4 additions & 4 deletions src/lib/WidgetContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ Widget* WidgetContainer::addAfter(Widget* anchor, Widget* widget, const std::str

Widget* WidgetContainer::addAfter(const std::string& anchor_name, Widget* widget, const std::string& name)
{
Widget* a = getWidget(anchor_name);
Widget* a = sfw::getWidget(anchor_name); //!! See #322 for why not just this->...
return a ? addAfter(a, widget, name) : add(widget, name);
}

Expand All @@ -187,8 +187,8 @@ Widget* WidgetContainer::addBefore(Widget* anchor, Widget* widget, const std::st
{
if (anchor == nullptr)
{
// addFirst
return insert_after(nullptr, widget, name);
// addFirst
return insert_after(nullptr, widget, name);
}
else if (is_child(anchor))
{
Expand All @@ -200,7 +200,7 @@ Widget* WidgetContainer::addBefore(Widget* anchor, Widget* widget, const std::st

Widget* WidgetContainer::addBefore(const std::string& anchor_name, Widget* widget, const std::string& name)
{
Widget* a = getWidget(anchor_name);
Widget* a = sfw::getWidget(anchor_name); //!! See #322 for why not just this->...
return a ? addBefore(a, widget, name) : add(widget, name);
}

Expand Down
Loading

0 comments on commit 9b46834

Please sign in to comment.