Lumiera  0.pre.03
»edit your freedom«
track-body.cpp
Go to the documentation of this file.
1 /*
2  TrackBody - track body area within the timeline display canvas
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 
36 #include "stage/gtk-base.hpp"
39 
40 #include "lib/util.hpp"
41 
42 using util::isnil;
43 using util::min;
44 
45 
46 namespace stage {
47 namespace timeline {
48 
49  namespace {
50  const uint DEFAULT_CONTENT_HEIGHT_px = 40;
51  const uint TIMELINE_BOTTOM_PADDING_px = 5;
52  }
53 
54 
55 
56 
57  TrackBody::TrackBody()
58  : contentHeight_{DEFAULT_CONTENT_HEIGHT_px}
59  , contentOffset_{0}
60  , startLine_{0}
61  , subTracks_{}
62  , rulers_{}
63  { }
64 
65 
66  TrackBody::~TrackBody()
67  { // indicate change of the global track structure
68  signalStructureChange_();
69  }
70 
71 
74 
75 
76  void
77  TrackBody::setTrackName (cuString& trackName)
78  {
79  TODO ("is the track name of any relevance for the TrackBody widget?");
80  }
81 
82 
83  /* ==== Interface: ViewHook ===== */
84 
85  void
86  TrackBody::hook (TrackBody& subBody)
87  {
88  subTracks_.push_back (&subBody);
89 
90  // notify presentation code of the changed structure
91  subBody.signalStructureChange_ = signalStructureChange_;
92  signalStructureChange_(); // this _is_ such a change
93  }
94 
95  void
96  TrackBody::remove (TrackBody& subBody)
97  {
98  util::removeall (subTracks_, &subBody);
99  signalStructureChange_();
100  }
101 
102  void
103  TrackBody::rehook (TrackBody& subBody) noexcept
104  {
105  util::removeall (subTracks_, &subBody);
106  subTracks_.push_back (&subBody);
107  signalStructureChange_();
108  }
109 
110 
111  /* ==== Code for vertical track display layout ==== */
112 
117  void
118  TrackBody::accommodateContentHeight(uint contentExtension)
119  {
120  if (contentExtension > contentHeight_)
121  contentHeight_ = contentExtension;
122  }
123 
131  uint
133  {
134  return calcContentHeight()
135  + calcSubtrackHeight();
136  }
137 
142  uint
144  {
145  return calcRulerHeight()
146  + contentHeight_ + decoration.content
147  + (isnil (subTracks_)? 0 // slope down to nested scope
148  : decoration.borders[0]);
149  }
150 
155  uint
157  { // „insider trick“ to include prelude padding on root track...
158  uint overviewHeight{startLine_== 0? // parent adds offset to startLine_ of any sub-track
159  decoration.topMar : 0};
160  for (auto& ruler : rulers_)
161  {
162  overviewHeight += ruler->calcHeight()
163  + ruler->getGapHeight()
164  + decoration.ruler;
165  }
166  return overviewHeight;
167  }
168 
169  uint
170  TrackBody::calcSubtrackHeight() const
171  {
172  uint heightSum{isnil (subTracks_)? 0 // approximate slope up (possibly exaggerated)
173  : decoration.borders[0] };
174  for (TrackBody* subTrack : subTracks_)
175  heightSum += subTrack->calcHeight();
176  return heightSum;
177  }
178 
179 
180  namespace {
188  inline uint
189  combinedSlopeHeight (uint depth)
190  {
191  if (depth==0) return 0;
192  depth = min (depth, TrackBody::decoration.borders.size() - 1);
193  return TrackBody::decoration.borders[depth];
194  }
195  }
196 
197 
212  uint
214  {
215  uint line=0;
216  bool topLevel = isnil (profile);
217  if (topLevel)
218  {
219  // global setup for the profile
220  line += decoration.topMar;
221  profile.append_prelude();
222  }
223  else
224  { // adjust if preceded by a combined up-slope
225  line += combinedSlopeHeight (profile.getPrecedingSlopeUp());
226  }
227 
228  // reserve space for the overview rulers
229  for (auto& ruler : rulers_)
230  {
231  uint rulerHeight = ruler->calcHeight();
232  uint gapHeight = ruler->getGapHeight();
233  line += rulerHeight+gapHeight + decoration.ruler;
234  profile.append_ruler (rulerHeight);
235  if (gapHeight > 0)
236  profile.append_gap (gapHeight);
237  }
238  if (topLevel)
239  {
240  // The first Profile elements are always visible on top;
241  // we render this prefix part on a separate drawing canvas,
242  profile.markPrefixEnd();
243  // ...and now we switch to the second, scrollable canvas,
244  // which uses its own local coordinates, thus restart Y-pos.
245  line = 0;
246  }
247  // mark offset of the actual content area relative to this track's top
248  this->contentOffset_ = line + decoration.trackPad;
249 
250  // allocate space for the track content
251  line += this->contentHeight_ + decoration.content;
252  profile.append_content (this->contentHeight_);
253 
254  // account for the space required by nested sub-tracks
255  if (not isnil (subTracks_))
256  {
257  // account for a single slope one step down
258  line += decoration.borders[0]; // (downward slopes are never combined)
259  profile.addSlopeDown();
260 
261  for (TrackBody* subTrack : subTracks_)
262  {
263  // (re)set the subTrack's start coordinates
264  // to reflect the allocation calculation done thus far
265  subTrack->startLine_ = this->startLine_ + line;
266  line += subTrack->establishTrackSpace (profile);
267  }
268 
269  profile.addSlopeUp(); // note: up-slopes might be combined
270  } // thus we'll add their contribution
271  // at the calling function one level higher
272  if (topLevel)
273  {
274  // adjust when reaching top-level after a combined up-slope
275  line += combinedSlopeHeight (profile.getPrecedingSlopeUp());
276 
277  line += decoration.botMar + TIMELINE_BOTTOM_PADDING_px;
278  profile.append_coda (TIMELINE_BOTTOM_PADDING_px);
279  }
280 
281  return line;
282  }
283 
284 
285 
286 }}// namespace stage::timeline
uint calcRulerHeight() const
sum up the vertical extension required by all overview rulers.
Definition: track-body.cpp:156
uint calcHeight() const
recursively calculate the height in pixels to display this track, including all nested sub-tracks and...
Definition: track-body.cpp:132
static Decoration decoration
storage for common style/padding settings
Definition: track-body.hpp:111
uint calcContentHeight() const
Definition: track-body.cpp:143
Borders borders
width of up to 6 levels of combined upward slope borders (defined in CSS)
Definition: track-body.hpp:79
Description of the structure and arrangement of tracks for display in the UI.
Lumiera GTK UI implementation root.
Definition: guifacade.cpp:46
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
void accommodateContentHeight(uint contentExtension)
ensure content with the given extension can be accommodated within this track&#39;s content area ...
Definition: track-body.cpp:118
uint combinedSlopeHeight(uint depth)
helper to get the width of combined slope borders.
Definition: track-body.cpp:189
uint establishTrackSpace(TrackProfile &)
recursively establish the screen space allocation for this structure of nested tracks.
Definition: track-body.cpp:213
Helper to organise and draw the space allocated for a fork of sub-tracks.
Definition: track-body.hpp:95
This helper class serves to manage the layout and display of the horizontally extended space of a "tr...
Configure additional vertical padding for the decorations added through CSS.
Definition: track-body.hpp:70
Abstraction to build the layout for the track spaces within timeline display.
A set of basic GTK includes for the UI.