Timeline: handle notification of structural updates

This commit is contained in:
Fischlurch 2019-06-20 18:40:36 +02:00
parent 83c462abc3
commit 77805a5c8c
12 changed files with 165 additions and 26 deletions

View file

@ -67,6 +67,30 @@ in response to a (population) diff message.
to be properly detached from signals. We want to ensure this happens as soon as the
child is taken out of service. Do not use an ``can collect garbage later'' approach.]
Signals
~~~~~~~
Basically, Signals are just typed callback functors. However, the *Sigc\++* library
helps to deal with the inherent danger of _dangling references,_ and it allows to
manage and disconnect signal attachments. Thus, whenever cross wiring beyond the
given model structure can be expected within the UI, usage of `sigc::signal<..>`
should be preferred.
NOTE: by itself, `sigc::signal` is a lightweight ref-counting smart-pointer.
.Conventions
- the names of signals are prefixed by the word `signal`.
- whenever a signal is used over several widgets and components,
there should be a _typedef_ for the signal type,
e.g. `using SignalBarf = sigc::signal<bool,Booh&>;`
- it is perfectly fine for a signal to be just a public member of some component,
especially when the sole purpose of that signal is for someone else to connect
or invoke it. However, when a signal is meant to be an internal implementation
detail, then better make it private, optinally exposing it via accessor function.
- a function intended to be connected to a signal is termed as ``Slot'', and its
name should be prefixed by the word `slot`, e.g. `bool slotBarf (Booh& moo);`
- such a slot function should be `noexcept`
Error handling
~~~~~~~~~~~~~~
Be aware that GTK is written in C. And while GTKmm has some safeguards in place,

View file

@ -248,6 +248,9 @@ namespace timeline {
, rulerCanvas_{makeRenderer<Grounding, RULER>(layout_,profile_), makeRenderer<Overlay, RULER>(layout_,profile_)}
, mainCanvas_ {makeRenderer<Grounding, BODY>(layout_,profile_), makeRenderer<Overlay, BODY>(layout_,profile_)}
{
// respond to any structure changes of the timeline by recomputing the TrackProfile
layout_.signalStructureChange_.connect (sigc::mem_fun (*this, &BodyCanvasWidget::slotStructureChange));
this->set_border_width (0);
this->property_expand() = true; // dynamically grab any available additional space
this->pack_start (rulerCanvas_);
@ -286,6 +289,14 @@ namespace timeline {
}
/** force rebuilding of theTrackProfile whenever the global timeline structure changes */
void
BodyCanvasWidget::slotStructureChange() noexcept
{
profile_.clear();
}
/**
*
*/

View file

@ -130,6 +130,7 @@ namespace timeline {
private:/* ===== Internals ===== */
void slotStructureChange() noexcept;
TrackProfile& establishTrackProfile();
};

View file

@ -60,6 +60,8 @@
#include "lib/util.hpp"
#include <sigc++/signal.h>
//#include <memory>
//#include <vector>
@ -107,6 +109,16 @@ namespace timeline {
/** the overall horizontal pixel span to cover by this timeline */
virtual PixSpan getPixSpan() =0;
using SignalStructureChange = sigc::signal<void>;
/**
* signal to be invoked whenever the virtual structure of the
* corresponding timeline changes, thus necessitating a new
* arrangement of the timeline layout.
*/
SignalStructureChange signalStructureChange_;
private:/* ===== Internals ===== */
};

View file

@ -32,6 +32,7 @@
#include "stage/gtk-base.hpp"
#include "stage/timeline/timeline-layout.hpp"
#include "stage/timeline/track-body.hpp"
//#include "stage/ui-bus.hpp"
//#include "lib/format-string.hpp"
@ -84,6 +85,10 @@ namespace timeline {
{
headerPane_.installForkRoot (head);
bodyCanvas_.installForkRoot (body);
// detect changes of the track structure
body.signalStructureChange_ = signalStructureChange_;
signalStructureChange_(); // this _is_ such a change
}

View file

@ -73,8 +73,8 @@ namespace timeline {
TrackBody::~TrackBody()
{
TODO ("detach from parent; store a functor or backreference");
{ // indicate change of the global track structure
signalStructureChange_();
}
@ -85,6 +85,18 @@ namespace timeline {
}
void
TrackBody::attachSubTrack (TrackBody* subBody)
{
REQUIRE (subBody);
subTracks_.push_back (subBody); /////////////////////////////////////////////////////TICKET #1199 : this can not possibly work; we need a way to retain the order of tracks, and we need to detach tracks...
// detect changes of the track structure
subBody->signalStructureChange_ = signalStructureChange_;
signalStructureChange_(); // this _is_ such a change
}
/**
* recursively calculate the height in pixels to display this track,
* including all nested sub-tracks

View file

@ -42,6 +42,7 @@
#include "stage/gtk-base.hpp"
#include "stage/timeline/ruler-track.hpp"
#include "stage/timeline/timeline-layout.hpp"
//#include "lib/util.hpp"
@ -86,10 +87,11 @@ namespace timeline {
~TrackBody();
void setTrackName (cuString&);
void establishTrackSpace (TrackProfile&);
void attachSubTrack (TrackBody*);
uint calcHeight();
void establishTrackSpace (TrackProfile&);
DisplayManager::SignalStructureChange signalStructureChange_;
private:/* ===== Internals ===== */

View file

@ -70,7 +70,10 @@ namespace timeline {
TrackPresenter::~TrackPresenter() { }
TrackPresenter::~TrackPresenter()
{
TODO ("find a way how to detach from parent tracks"); ////////////////////////////////////////////TICKET #1199 : how to deal with re-ordering of tracks?
}

View file

@ -92,6 +92,7 @@ namespace timeline {
~DisplayFrame()
{
// Note: ~TrackBody triggers DisplayManager::signalStructureChange_()
TODO ("cause the managed presentation elements to detach from their parents");
} ///////////////////////////////////TICKET #1198 -- clarify to what extent esp. the header widgets need to be actively removed from the display structure. Is it sufficient just to kill the TrackHeadWidget
@ -103,8 +104,9 @@ namespace timeline {
}
void
injectSubTrack (TrackHeadWidget& head, TrackBody& body)
injectSubTrack (TrackHeadWidget& subHead, TrackBody& subBody)
{
body.attachSubTrack (&subBody);
UNIMPLEMENTED ("inject the widgets to represent a nested sub-track within this timeline track display frame");
}
};

View file

@ -97,6 +97,12 @@ namespace timeline {
return elements.empty();
}
void
clear() noexcept
{
elements.clear();
}
void
performWith (ProfileInterpreter& interpreter)
{

View file

@ -128,10 +128,12 @@ namespace widget {
using Entry = std::pair<Mark,Mark>;
using TextWidget = model::FlashDeco<Gtk::TextView>;
using SignalErrorChanged = sigc::signal<void, bool>;
vector<Entry> errorMarks_;
TextWidget textLog_;
sigc::signal<void, bool> errorChangedSignal_;
SignalErrorChanged errorChangedSignal_;
public:
@ -291,7 +293,7 @@ namespace widget {
}
/** signal fired when error state changes */
sigc::signal<void,bool>
SignalErrorChanged
signalErrorChanged()
{
return errorChangedSignal_;

View file

@ -18447,7 +18447,10 @@
<node CREATED="1480124145476" ID="ID_1268974326" MODIFIED="1557498707225" TEXT="f&#xfc;r einen ganzen Scope"/>
<node CREATED="1480124153979" ID="ID_887707794" MODIFIED="1557498707225" TEXT="stellt fest, was gezeigt werden mu&#xdf;"/>
<node CREATED="1480124174144" ID="ID_1145067443" MODIFIED="1557498707225" TEXT="synthetisiert Anzeige-Parameter (z.B: Koordinaten, Z-Ordnung)"/>
<node CREATED="1480124235128" ID="ID_162610711" MODIFIED="1557498707225" TEXT="mu&#xdf; inkrementell arbeiten und bestehende Widgets anpassen"/>
<node CREATED="1480124235128" ID="ID_162610711" MODIFIED="1561047428001" TEXT="mu&#xdf; inkrementell arbeiten und bestehende Widgets anpassen">
<arrowlink COLOR="#d6677c" DESTINATION="ID_1660005887" ENDARROW="Default" ENDINCLINATION="130;-668;" ID="Arrow_ID_1942101483" STARTARROW="None" STARTINCLINATION="613;0;"/>
<icon BUILTIN="yes"/>
</node>
</node>
</node>
</node>
@ -19139,9 +19142,10 @@
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1560698483596" ID="ID_368409585" MODIFIED="1560698492402" TEXT="Infrastruktur f&#xfc;r Status-&#xc4;nderungen">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1560698506599" ID="ID_1679016153" MODIFIED="1560698513827" TEXT="notwendig">
<node COLOR="#338800" CREATED="1560698483596" ID="ID_368409585" MODIFIED="1561048727700" TEXT="Infrastruktur f&#xfc;r Status-&#xc4;nderungen">
<icon BUILTIN="button_ok"/>
<node CREATED="1560698506599" ID="ID_1679016153" MODIFIED="1561048713811" TEXT="notwendig">
<icon BUILTIN="info"/>
<node CREATED="1560698514618" ID="ID_424874311" MODIFIED="1560698517787" TEXT="Benachrichtigung">
<node CREATED="1560698522830" ID="ID_771595987" MODIFIED="1560698534464" TEXT="initial..."/>
<node CREATED="1560698535135" ID="ID_499624854" MODIFIED="1560698542403" TEXT="bei neuen Elementen"/>
@ -19150,8 +19154,9 @@
</node>
<node CREATED="1560698518159" ID="ID_625626693" MODIFIED="1560698521434" TEXT="Aktionen"/>
</node>
<node CREATED="1560698568536" ID="ID_1734037059" MODIFIED="1560698572183" TEXT="Mechanismen">
<node CREATED="1560698573838" ID="ID_546774892" MODIFIED="1560698581946" TEXT="Call-chain bauen">
<node COLOR="#435e98" CREATED="1560698568536" ID="ID_1734037059" MODIFIED="1561048706720" TEXT="Mechanismen">
<node CREATED="1560698573838" ID="ID_546774892" MODIFIED="1561042610462" TEXT="Call-chain bauen">
<icon BUILTIN="button_cancel"/>
<node CREATED="1560698613458" ID="ID_1618171484" MODIFIED="1560698618101" TEXT="direkte Parent-Links">
<node CREATED="1560698643705" ID="ID_1904845397" MODIFIED="1560698651645" TEXT="eine Menge Aufwand"/>
<node CREATED="1560698652882" ID="ID_314410558" MODIFIED="1560698659496" TEXT="komplex instand zu halten"/>
@ -19160,7 +19165,7 @@
<node CREATED="1560698618689" ID="ID_231185798" MODIFIED="1560698625444" TEXT="notification-Callbacks">
<node CREATED="1560698764250" ID="ID_926915515" MODIFIED="1560698770146" TEXT="Publisher-Subscriber"/>
<node CREATED="1560698668089" ID="ID_1609727906" MODIFIED="1560698674278" TEXT="ebenso problematisch"/>
<node CREATED="1560698675226" ID="ID_1528720357" MODIFIED="1560698750879" TEXT="Signale sind aber Callbacks....">
<node CREATED="1560698675226" ID="ID_1528720357" MODIFIED="1561042754690" TEXT="Signale sind aber Callbacks....">
<richcontent TYPE="NOTE"><html>
<head>
@ -19172,28 +19177,79 @@
</body>
</html>
</richcontent>
<arrowlink COLOR="#fcc76b" DESTINATION="ID_64926941" ENDARROW="Default" ENDINCLINATION="15;-34;" ID="Arrow_ID_1800480673" STARTARROW="None" STARTINCLINATION="-57;0;"/>
<arrowlink COLOR="#fcc76b" DESTINATION="ID_64926941" ENDARROW="Default" ENDINCLINATION="24;-35;" ID="Arrow_ID_1800480673" STARTARROW="None" STARTINCLINATION="-57;0;"/>
<icon BUILTIN="idea"/>
</node>
</node>
</node>
<node CREATED="1560698625944" ID="ID_64926941" MODIFIED="1560698753534" TEXT="ein spezielles Signal hierf&#xfc;r">
<linktarget COLOR="#fcc76b" DESTINATION="ID_64926941" ENDARROW="Default" ENDINCLINATION="15;-34;" ID="Arrow_ID_1800480673" SOURCE="ID_1528720357" STARTARROW="None" STARTINCLINATION="-57;0;"/>
<node CREATED="1560698805704" ID="ID_1490359824" MODIFIED="1560698813328" TEXT="wer tr&#xe4;gt es?"/>
<node CREATED="1560698814327" ID="ID_44788176" MODIFIED="1560698819234" TEXT="wie wird es zug&#xe4;nglich?"/>
<node COLOR="#435e98" CREATED="1560698625944" ID="ID_64926941" MODIFIED="1561048698955" TEXT="ein spezielles Signal hierf&#xfc;r">
<linktarget COLOR="#fcc76b" DESTINATION="ID_64926941" ENDARROW="Default" ENDINCLINATION="24;-35;" ID="Arrow_ID_1800480673" SOURCE="ID_1528720357" STARTARROW="None" STARTINCLINATION="-57;0;"/>
<icon BUILTIN="forward"/>
<node CREATED="1560698805704" ID="ID_1490359824" MODIFIED="1561042627539" TEXT="wer tr&#xe4;gt es?">
<arrowlink DESTINATION="ID_1799554074" ENDARROW="Default" ENDINCLINATION="93;0;" ID="Arrow_ID_576839005" STARTARROW="None" STARTINCLINATION="169;0;"/>
</node>
<node CREATED="1560698814327" ID="ID_44788176" MODIFIED="1561048694418" TEXT="wie wird es zug&#xe4;nglich?">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<ul>
<li>
per direktem Zugriff via Interface DisplayManager
</li>
<li>
per Zuweisung an die einzelnen TrackBody (signal == smart-ptr!)
</li>
</ul>
</body>
</html>
</richcontent>
</node>
</node>
<node CREATED="1561036803347" ID="ID_281948392" MODIFIED="1561036824656" TEXT="Sigc-Signal ist ein (ref-counting) smart-ptr">
<icon BUILTIN="idea"/>
</node>
</node>
<node CREATED="1561036911418" ID="ID_1948376179" MODIFIED="1561036913846" TEXT="Struktur">
<node CREATED="1561036914961" ID="ID_318008470" MODIFIED="1561036944385" TEXT="der DisplayManager tr&#xe4;gt das &quot;Anker&quot;-Signal">
<node CREATED="1561036987264" ID="ID_1799554074" MODIFIED="1561037495245" TEXT="signalStructureChange"/>
<node COLOR="#338800" CREATED="1561036911418" ID="ID_1948376179" MODIFIED="1561048719567" TEXT="Struktur">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1561036914961" ID="ID_318008470" MODIFIED="1561042586692" TEXT="der DisplayManager tr&#xe4;gt das &quot;Anker&quot;-Signal">
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1561036987264" ID="ID_1799554074" MODIFIED="1561042627539" TEXT="signalStructureChange">
<linktarget COLOR="#a9b4c1" DESTINATION="ID_1799554074" ENDARROW="Default" ENDINCLINATION="93;0;" ID="Arrow_ID_576839005" SOURCE="ID_1490359824" STARTARROW="None" STARTINCLINATION="169;0;"/>
<icon BUILTIN="info"/>
</node>
<node CREATED="1561036945238" ID="ID_932981139" MODIFIED="1561036956895" TEXT="Nutzer kopieren es sich hiervon"/>
<node CREATED="1561037513769" ID="ID_1240081490" MODIFIED="1561037547694" TEXT="das BodyCanvasWidget h&#xe4;ngt daran seinen Slot">
<node CREATED="1561037555636" ID="ID_830580579" MODIFIED="1561037559431" TEXT="slotStructureChange"/>
</node>
<node COLOR="#338800" CREATED="1561036945238" ID="ID_932981139" MODIFIED="1561047017981" TEXT="Nutzer kopieren es sich hiervon">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1561037513769" ID="ID_1240081490" MODIFIED="1561042582058" TEXT="das BodyCanvasWidget h&#xe4;ngt daran seinen Slot">
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1561037555636" ID="ID_830580579" MODIFIED="1561042600545" TEXT="slotStructureChange">
<icon BUILTIN="info"/>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1561047296781" ID="ID_1660005887" MODIFIED="1561047428001" TEXT="Neuberechnung / Reihenfolge">
<linktarget COLOR="#d6677c" DESTINATION="ID_1660005887" ENDARROW="Default" ENDINCLINATION="130;-668;" ID="Arrow_ID_1942101483" SOURCE="ID_162610711" STARTARROW="None" STARTINCLINATION="613;0;"/>
<icon BUILTIN="flag-yellow"/>
<node CREATED="1561047320822" ID="ID_652813348" MODIFIED="1561047325927" TEXT="Grunds&#xe4;tzliches">
<node CREATED="1561047326773" ID="ID_455856045" MODIFIED="1561047353557" TEXT="wie wird die Reihenfolge der Tracks festgelegt?">
<icon BUILTIN="help"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1561047338983" ID="ID_608382986" MODIFIED="1561047362267" TEXT="wie werden &#xc4;nderungen dieser realisiert?">
<icon BUILTIN="help"/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1561048744043" ID="ID_1401239177" MODIFIED="1561048756692" TEXT="Konzept kl&#xe4;ren">
<icon BUILTIN="flag-pink"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1561048792468" ID="ID_638394722" MODIFIED="1561048797689" TEXT="#1199 handle Timeline layout changes">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1561047369284" ID="ID_488158262" MODIFIED="1561047377394" TEXT="Implementierung">
<icon BUILTIN="hourglass"/>
</node>
</node>
</node>
@ -20890,6 +20946,9 @@
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1541814988759" ID="ID_327310802" MODIFIED="1557498707230" TEXT="mu&#xdf; der TrackBody seinen Namen kennen?">
<icon BUILTIN="help"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1561047126901" ID="ID_376833399" MODIFIED="1561047145040" TEXT="Ordnung und Entfernen von sub-Track Bodies!!">
<icon BUILTIN="flag-pink"/>
</node>
</node>
</node>
<node CREATED="1541861473496" ID="ID_457526663" MODIFIED="1557498707230" TEXT="draw">