Lumiera  0.pre.03
»edit your freedom«
track-presenter.hpp
Go to the documentation of this file.
1 /*
2  TRACK-PRESENTER.hpp - presentation control element for a track within the timeline
3 
4  Copyright (C) Lumiera.org
5  2016, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 */
22 
23 
96 #ifndef STAGE_TIMELINE_TRACK_PRESENTER_H
97 #define STAGE_TIMELINE_TRACK_PRESENTER_H
98 
99 #include "stage/gtk-base.hpp"
100 #include "include/ui-protocol.hpp"
101 #include "stage/model/view-hook.hpp"
108 #include "lib/time/timevalue.hpp"
109 #include "lib/iter-explorer.hpp"
110 #include "lib/util-coll.hpp"
111 
112 #include "lib/nocopy.hpp"
113 
114 #include <optional>
115 #include <utility>
116 #include <vector>
117 
118 
119 
120 namespace stage {
121 namespace timeline {
122 
123  using std::vector;
124  using std::unique_ptr;
125  using std::make_unique;
126 
127  using lib::time::TimeSpan;
129  using lib::diff::collection;
130  using lib::explore;
131  using util::max;
132 
133 
134 
146  template<class WID>
148  : public model::CanvasHook<WID>
149  {
150  model::CanvasHook<WID>& refHook_;
151 
152 
153  /* ==== Interface: ViewHook ===== */
154 
155  void
156  hook (WID& widget, int xPos=0, int yPos=0) override
157  {
158  refHook_.hook (widget, hookAdjX (xPos), hookAdjY (yPos));
159  }
160 
161  void
162  move (WID& widget, int xPos, int yPos) override
163  {
164  refHook_.move (widget, hookAdjX (xPos), hookAdjY (yPos));
165  }
166 
167  void
168  remove (WID& widget) override
169  {
170  refHook_.remove (widget);
171  }
172 
175  getAnchorHook() noexcept override
176  {
177  return this->refHook_;
178  }
179 
180  protected: /* === extended Interface for relative canvas hook === */
181  virtual int hookAdjX (int xPos) =0;
182  virtual int hookAdjY (int yPos) =0;
183 
186  getMetric() const override
187  {
188  return refHook_.getMetric();
189  }
190 
191  public:
193  : refHook_{baseHook.getAnchorHook()}
194  { }
195  };
196 
197 
198 
199  using PFork = unique_ptr<TrackPresenter>;
200  using PClip = unique_ptr<ClipPresenter>;
201  using PMark = unique_ptr<MarkerWidget>;
202  using PRuler = unique_ptr<RulerTrack>;
203 
215  , public DisplayViewHooks
216  , public RelativeCanvasHook<Gtk::Widget>
217  {
220 
221  /* === extended Interface for relative view/canvas hook === */
222 
223  int hookAdjX (int xPos) override { return xPos; };
224  int hookAdjY (int yPos) override { return yPos + body_.getContentOffsetY(); };
225 
226  public: /* ==== Interface: DisplayViewHooks===== */
227 
228  model::ViewHook<TrackHeadWidget>& getHeadHook() override { return head_; };
229  model::ViewHook<TrackBody>& getBodyHook() override { return body_; };
230  model::CanvasHook<Gtk::Widget>& getClipHook() override { return *this; };
231 
232  public:
233  DisplayFrame (DisplayViewHooks& displayAnchor)
234  : RelativeCanvasHook{displayAnchor.getClipHook()}
235  , head_{displayAnchor.getHeadHook()}
236  , body_{displayAnchor.getBodyHook()}
237  { }
238 
239  void
240  setTrackName (cuString& name)
241  {
242  head_.setTrackName (name);
243  body_.setTrackName (name);
244  }
245 
246  vector<unique_ptr<RulerTrack>>&
247  bindRulers()
248  {
249  return body_.bindRulers();
250  }
251 
252  void establishExtension (vector<PClip>&, vector<PMark>&);
253  void sync_and_balance (DisplayEvaluation&);
254  };
255 
256 
262  : public model::Controller
263  , public LayoutElement
264  {
265  DisplayFrame display_;
266 
267  vector<PFork> subFork_;
268  vector<PMark> markers_;
269  vector<PClip> clips_;
270 
271 
272  public:
278  TrackPresenter (ID id, ctrl::BusTerm& nexus, DisplayViewHooks& displayAnchor)
279  : Controller{id, nexus}
280  , display_{displayAnchor}
281  , subFork_{}
282  , markers_{}
283  , clips_{}
284  {
285  setTrackName (id.getSym()); // fallback initialise track-name from human-readable ID symbol
286  }
287 
288 
290  virtual void buildMutator (lib::diff::TreeMutator::Handle) override;
291 
292 
293  protected: /* ==== Interface: LayoutElement ===== */
294 
295  void establishLayout (DisplayEvaluation&) override;
296  void completeLayout (DisplayEvaluation&) override;
297 
298  private:/* ===== Internals ===== */
299 
301  void
303  {
304  display_.setTrackName (name);
305  }
306 
307  void relinkContents ();
308  };
309 
310 
311 
312 
322  inline void
324  {
325  buffer.emplace(
327  .attach (collection(display_.bindRulers())
328  .isApplicableIf ([&](GenNode const& spec) -> bool
329  { // »Selector« : require object-like sub scope with type-field "Ruler"
330  return TYPE_Ruler == spec.data.recordType();
331  })
332  .constructFrom ([&](GenNode const& spec) -> PRuler
333  { // »Constructor« : how to attach a new ruler track
334  return make_unique<RulerTrack> (spec.idi, this->uiBus_, *this);
335  }))
336  .attach (collection(markers_)
337  .isApplicableIf ([&](GenNode const& spec) -> bool
338  { // »Selector« : require object-like sub scope with type-field "Marker"
339  return TYPE_Marker == spec.data.recordType();
340  })
341  .constructFrom ([&](GenNode const& spec) -> PMark
342  {
343  return make_unique<MarkerWidget> (spec.idi, this->uiBus_);
344  }))
345  .attach (collection(clips_)
346  .isApplicableIf ([&](GenNode const& spec) -> bool
347  { // »Selector« : require object-like sub scope with type-field "Clip"
348  return TYPE_Clip == spec.data.recordType();
349  })
350  .constructFrom ([&](GenNode const& spec) -> PClip
351  {
352  std::optional<TimeSpan> timing = spec.retrieveAttribute<TimeSpan> (string{ATTR_timing});
353  return make_unique<ClipPresenter> (spec.idi, this->uiBus_, display_.getClipHook(), timing);
354  }))
355  .attach (collection(subFork_)
356  .isApplicableIf ([&](GenNode const& spec) -> bool
357  { // »Selector« : require object-like sub scope with type-field "Fork"
358  return TYPE_Fork == spec.data.recordType();
359  })
360  .constructFrom ([&](GenNode const& spec) -> PFork
361  {
362  return make_unique<TrackPresenter> (spec.idi, uiBus_, this->display_);
363  }))
364  .change(ATTR_name, [&](string val)
365  { // »Attribute Setter« : receive a new value for the track name field
366  this->setTrackName (val);
367  }));
368  }
369 
370 
371 
373  inline void
375  {
376  ASSERT (displayEvaluation.isCollectPhase());
377  display_.establishExtension (clips_, markers_);
378  for (auto& subTrack: subFork_)
379  subTrack->establishLayout (displayEvaluation);
380  }
381 
383  inline void
385  {
386  ASSERT (not displayEvaluation.isCollectPhase());
387  // recursion first, so all sub-Tracks are already balanced
388  for (auto& subTrack: subFork_)
389  subTrack->completeLayout (displayEvaluation);
390  this->relinkContents();
391  // re-sync and match the header / body display
392  display_.sync_and_balance (displayEvaluation);
393  }
394 
404  inline void
405  DisplayFrame::establishExtension (vector<PClip>& clips, vector<PMark>&)
406  {
407  uint maxVSize = max (explore (clips)
408  .transform([](PClip const& clip)
409  {
410  return clip->determineRequiredVerticalExtension();
411  }));
412  maxVSize = max (maxVSize, head_.getContentHeight());
413  this->body_.accommodateContentHeight (maxVSize);
414  this->head_.accommodateContentHeight (maxVSize);
415  }
416 
421  inline void
423  {
424  for (auto& clip: clips_)
425  clip->relink();
426  for (auto& mark: markers_)
427  mark->relink();
428  }
429 
430 
448  inline void
450  {
451  uint contentHeight = body_.calcContentHeight();
452  uint overallHeight = contentHeight + body_.calcSubtrackHeight();
453  head_.syncSubtrackStartHeight (contentHeight);
454  head_.accommodateOverallHeight (overallHeight);
455  }
456 
457 
458 
459 
460 
461 }}// namespace stage::timeline
462 #endif /*STAGE_TIMELINE_TRACK_PRESENTER_H*/
void relinkContents()
second pass of the DisplayEvaluation: reassemble content to match adjusted layout ...
A widget attached onto a display canvas or similar central presentation context.
Definition: view-hook.hpp:123
Hard wired key constants and basic definitions for communication with the GUI.
Widget to show a marker at various places.
model::DisplayMetric & getMetric() const override
delegating layout metric to the root canvas
connection point at the UI-Bus.
Definition: bus-term.hpp:105
auto explore(IT &&srcSeq)
start building a IterExplorer by suitably wrapping the given iterable source.
Mix-in interface to allow for concrete CanvasHooked widgets to adapt themselves to the metric current...
Definition: canvas-hook.hpp:82
void sync_and_balance(DisplayEvaluation &)
re-flow and adjust after the global layout has been established At this point we can assume that both...
Any copy and copy construction prohibited.
Definition: nocopy.hpp:46
void setTrackName(string name)
invoked via diff to show a (changed) track name
void establishLayout(DisplayEvaluation &) override
handle Phase-1 of the DisplayEvaluation pass for this track and its sub-tracks.
Allow widgets to connect to a common shared presentation context.
void establishExtension(vector< PClip > &, vector< PMark > &)
Find out about the vertical extension of a single track display.
virtual DisplayMetric & getMetric() const =0
access the component to handle layout metric
Interface to represent _"some presentation layout entity",_ with the ability to place widgets (manage...
A View-Model entity to represent a timeline track in the UI.
A collaboration to establish a globally coherent timeline display layout.
static Builder< TreeMutator > build()
DSL: start building a custom adapted tree mutator, where the operations are tied by closures or wrapp...
SUB & emplace(SUB &&implementation)
move-construct an instance of a subclass into the opaque buffer
Widget to represent a track head with placement parameters, within the timeline header pane...
Visitor and state holder for a collaborative layout adjustment pass.
Mix-Ins to allow or prohibit various degrees of copying and cloning.
A handle to allow for safe »remote implantation« of an unknown subclass into a given opaque InPlaceBu...
Definition: record.hpp:113
virtual CanvasHook< WID > & getAnchorHook() noexcept
Anchor point to build chains of related View Hooks.
Lumiera GTK UI implementation root.
Definition: guifacade.cpp:46
Interface: a compound of anchoring facilities.
model::CanvasHook< WID > & getAnchorHook() noexcept override
allow to build a derived relative hook with different offset
TrackPresenter(ID id, ctrl::BusTerm &nexus, DisplayViewHooks &displayAnchor)
Presentation control element to model and manage a clip within the timeline UI.
std::optional< X > retrieveAttribute(string key) const
mismatch tolerant convenience shortcut to peek into the attributes of a nested Record ...
Definition: gen-node.hpp:809
This helper class serves to manage the layout and display of the horizontally extended space of a "tr...
string recordType() const
peek into the type field of a nested Record<GenNode>
Definition: gen-node.hpp:769
Reference frame to organise the presentation related to a specific Track in the Timeline-GUI.
Special CanvasHook decorator to apply a (dynamic) offset when attaching or moving Widgets on the shar...
virtual void buildMutator(lib::diff::TreeMutator::Handle) override
set up a binding to respond to mutation messages via UiBus
Some small helpers and convenience shortcuts to ease working with collections and sequences (given by...
void completeLayout(DisplayEvaluation &) override
handle Phase-2 (collect/balancing phase) for this track and its sub-tracks.
A time interval anchored at a specific point in time.
Definition: timevalue.hpp:582
auto collection(COLL &coll)
Entry point to a nested DSL for setup and configuration of a collection binding.
Building tree expanding and backtracking evaluations within hierarchical scopes.
a family of time value like entities and their relationships.
Common Abstraction of all sub-controller, coordinated by the UI-Bus.
A set of basic GTK includes for the UI.
ElementBoxWidget::Config::Qualifier name(string id)
define the name-ID displayed in the caption
generic data element node within a tree
Definition: gen-node.hpp:231
Customisable intermediary to abstract mutating operations on arbitrary, hierarchical object-like data...