diff --git a/src/gui/model/w-link.hpp b/src/gui/model/w-link.hpp index 552f2e9c9..923f0657f 100644 --- a/src/gui/model/w-link.hpp +++ b/src/gui/model/w-link.hpp @@ -25,7 +25,14 @@ ** A smart link to an GTK widget with automatic disconnection. ** Allows to hold a connection to a [trackable] without taking ownership. ** The link is statefull, can be reconnected, and automatically transitions - ** into disconnected state when the target dies. + ** into disconnected state when the target dies. The link state can be tested + ** by `bool` conversion. WLink objects are fully copyable, and each copy has + ** its own attachment state and can be reconnected independently. + ** + ** The purpose of WLink is to support collaborations between controllers and + ** widgets or between widgets. Whenever some logic works with or relies on + ** some other UI entity -- which it might even _"allocate"_ -- without taking + ** ownership, the relation can be implemented with a WLink. ** ** @warning this class is not threadsafe, because lib SigC++ is not either, ** and it can only be used reliably from within the GUI thread. @@ -52,6 +59,7 @@ #include "lib/error.hpp" +#include "lib/format-string.hpp" #include #include @@ -60,7 +68,10 @@ namespace gui { namespace model { - using lumiera::error::LUMIERA_ERROR_BOTTOM_VALUE; + namespace error = lumiera::error; + using error::LUMIERA_ERROR_BOTTOM_VALUE; + + using util::_Fmt; /** @@ -68,6 +79,7 @@ namespace model { * Automatically installs a callback to switch this link into detached state * when the target (widget) is destroyed. * @warning _not_ threadsafe + * @note only EX_SANE, since attaching, detaching and swapping might throw. */ template class WLink @@ -81,9 +93,12 @@ namespace model { public: ~WLink() { - this->clear(); + try { + this->clear(); + } + ERROR_LOG_AND_IGNORE (gui, "Detaching managed WLink from Widget") } - WLink() + WLink() noexcept : widget_{nullptr} { } @@ -103,20 +118,28 @@ namespace model { } WLink& - operator= (WLink other) + operator= (WLink other) ///< @warning only EX_SANE { swap (*this, other); return *this; } + /** swap the pointees, including callback registration. + * @warning only EX_SANE. + * Might leave the following intermediary states + * - only r was detached + * - both r and l are detached + * - both detached, but only l attached to the former target of r, + * while the former target of l is now completely detached. + */ friend void swap (WLink& l, WLink& r) { TAR* tl = l.widget_; TAR* tr = r.widget_; if (tl == tr) return; - l.clear(); r.clear(); + l.clear(); if (tr) l.widget_ = l.attachTo (*tr); if (tl) r.widget_ = r.attachTo (*tl); } @@ -143,22 +166,26 @@ namespace model { } - /** detach and deactivate this link */ + /** detach and deactivate this link + * @note EX_STRONG (but only under the assumption that also + * sigc::trackable::remove_destroy_notify_cllback is EX_STRONG) + */ void - clear() noexcept + clear() { if (widget_) - try { - widget_->remove_destroy_notify_callback (&widget_); - } - catch(...) { } + widget_->remove_destroy_notify_callback (&widget_); widget_ = nullptr; } /** (re)connect this smart link to the given target. - * Any previously existing link is detached beforehand */ + * Any previously existing link is detached beforehand + * @note EX_SANE only (assuming `sigc::trackable` is sane) + * Might leave this WLink in disconnected state when throwing + * @throws error::External when registration with `sigc::trackable` fails + */ void - connect (TAR& otherTarget) noexcept + connect (TAR& otherTarget) { if (widget_ == &otherTarget) return; clear(); @@ -170,15 +197,16 @@ namespace model { __ensureAlive() const { if (not widget_) - throw lumiera::error::State ("zombie widget encountered" - , LERR_(BOTTOM_VALUE)); + throw error::State ("zombie widget encountered" + , LERR_(BOTTOM_VALUE)); } /** @internal installs the necessary callback * to detach this link in case the target is destroyed + * @note EX_STRONG */ TAR* - attachTo (TAR& target) noexcept + attachTo (TAR& target) { try { target.add_destroy_notify_callback (&widget_ @@ -191,9 +219,14 @@ namespace model { }); return ⌖ } - catch(...) + catch (std::exception& problem) { - return nullptr; + throw error::External (problem, _Fmt{"WLink could not attach to %s. Problem is %s"} % target % problem); + } + catch (...) + { + ERROR (gui, "Unknown exception while attaching WLink"); + throw error::External (_Fmt{"WLink could not attach to %s due to unidentified Problems"} % target); } } }; diff --git a/tests/51-gui-model.tests b/tests/51-gui-model.tests index 22f7cf813..01bf86b76 100644 --- a/tests/51-gui-model.tests +++ b/tests/51-gui-model.tests @@ -17,6 +17,16 @@ return: 0 END +PLANNED "low-level widget access" ElementAccess_test <&&> (lu); CHECK ((int)uu->val == l1->val); - CHECK (not lu); + CHECK (not lu); // assignment was actually a move + + // even the subversively attached link is managed properly + uu.reset(); + CHECK (not l1); + + // others unaffected... + CHECK (not l2); + CHECK (l3); } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index cc4018dac..302e53143 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -3174,13 +3174,13 @@ - + - - - - - + + + + + @@ -3218,8 +3218,8 @@ - - + + @@ -3233,6 +3233,15 @@ + + + + + + + + +