WLink: finished incl. exception handling guarantees and documentation
This commit is contained in:
parent
ae26012bf5
commit
a74dc596ce
4 changed files with 91 additions and 29 deletions
|
|
@ -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 <type_traits>
|
||||
#include <sigc++/trackable.h>
|
||||
|
|
@ -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 TAR>
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,6 +17,16 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
PLANNED "low-level widget access" ElementAccess_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "managed link to widget" WLink_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "storage record for captured UI state" StateMapGroupingStorage_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ namespace test {
|
|||
}
|
||||
|
||||
|
||||
/** @test registration state is properly handled on copy, move and swap */
|
||||
void
|
||||
verify_copy()
|
||||
{
|
||||
|
|
@ -194,10 +195,19 @@ namespace test {
|
|||
// l1 = uu;
|
||||
// l1.connect(*uu);
|
||||
|
||||
// but it is a compile time check...
|
||||
// But it is a compile time check...
|
||||
// At runtime, only the bare pointer is managed
|
||||
l1 = reinterpret_cast<WLink<Wint>&&> (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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3174,13 +3174,13 @@
|
|||
<node CREATED="1534336320373" ID="ID_708892482" MODIFIED="1534336324377" TEXT="Grundbausteine"/>
|
||||
<node CREATED="1534336325916" ID="ID_762624766" MODIFIED="1534336335351" TEXT="Bindeglieder"/>
|
||||
<node CREATED="1534336335963" ID="ID_1176525116" MODIFIED="1534336338575" TEXT="Werkzeug">
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1534334810537" ID="ID_1326202399" MODIFIED="1534340000558" TEXT="Link zum Widget">
|
||||
<node COLOR="#338800" CREATED="1534334810537" FOLDED="true" ID="ID_1326202399" MODIFIED="1534436661859" TEXT="Link zum Widget">
|
||||
<linktarget COLOR="#a66b86" DESTINATION="ID_1326202399" ENDARROW="Default" ENDINCLINATION="279;-1333;" ID="Arrow_ID_664593340" SOURCE="ID_203145360" STARTARROW="None" STARTINCLINATION="-1296;0;"/>
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1534334914667" ID="ID_511749457" MODIFIED="1534334932930" TEXT="smart-Ref">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1534334939159" ID="ID_1853703071" MODIFIED="1534335238329" TEXT="leer konstruierbar">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1534334914667" ID="ID_511749457" MODIFIED="1534436573129" TEXT="smart-Ref">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1534334939159" ID="ID_1853703071" MODIFIED="1534436570387" TEXT="leer konstruierbar">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1534334944207" ID="ID_1989000221" MODIFIED="1534342998767" TEXT="bool testbar">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
|
|
@ -3218,8 +3218,8 @@
|
|||
</richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1534336127895" HGAP="21" ID="ID_352867763" MODIFIED="1534340006778" TEXT="WLink_test" VSHIFT="9">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#338800" CREATED="1534336127895" HGAP="21" ID="ID_352867763" MODIFIED="1534436566889" TEXT="WLink_test" VSHIFT="9">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1534340031277" ID="ID_506504727" MODIFIED="1534341378412" TEXT="test-dummy-trackable">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
|
|
@ -3233,6 +3233,15 @@
|
|||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1534436582390" ID="ID_880451727" MODIFIED="1534436621371" TEXT="nur EX_SANE">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1534436589806" ID="ID_1530771493" MODIFIED="1534436645751" TEXT="unter der Annahme, daß sigc::trackable EX_SANE ist">
|
||||
<icon BUILTIN="info"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1534436609323" ID="ID_1966799512" MODIFIED="1534436641103" TEXT="nicht wirklich geprüft">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
|
|||
Loading…
Reference in a new issue