Lumiera  0.pre.03
»edit your freedom«
clip-widget.cpp
Go to the documentation of this file.
1 /*
2  ClipWidget - display of a clip in timeline or media bin view
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 
93 #include "stage/gtk-base.hpp"
96 
97 #include "lib/format-string.hpp"
98 
99 #include "lib/util.hpp"
100 
101 #include <utility>
102 
103 
104 
105 using util::_Fmt;
106 using lib::time::TimeVar;
107 using util::unConst;
108 using std::optional;
109 
110 
111 namespace stage {
112 namespace timeline {
113  namespace error = lumiera::error;
114 
115  const int ClipDelegate::defaultOffsetY{0};
116  const string ClipDelegate::defaultName{_("clip")};
117 
118 
119  ClipDelegate::ClipDelegate() { }
121 
122 
123  namespace {// details of concrete clip appearance styles...
124 
127 
128 
129  class ClipData
130  : public ClipDelegate
132  {
133  TimeVar start_;
134  TimeVar dur_;
135 
136  public: /* === Partial implementation of ClipDelegate === */
137  TimeVar&
138  accessStartTime() override
139  {
140  return start_;
141  }
142 
143  TimeVar&
144  accessDuration() override
145  {
146  return this->dur_;
147  }
148 
149 
150  public:
152  : ClipDelegate{}
153  , start_{timings.start()}
154  , dur_{timings.duration()}
155  { }
156 
157  ClipData (ClipData&&) = default;
158  };
159 
160 
166  : public ClipData
167  {
168  WidgetHook& display_;
169  uString clipName_;
170 
171  /* === Interface ClipDelegate === */
172 
173  Appearance
174  currentAppearance() const override
175  {
176  return Appearance::PENDING;
177  }
178 
179  Appearance
181  {
182  return currentAppearance();
183  }
184 
185  cuString
186  getClipName() const override
187  {
188  return clipName_;
189  }
190 
191  void
192  setClipName(cuString& newName) override
193  {
194  clipName_ = newName;
195  }
196 
197 
202  uint
203  calcRequiredHeight() const override
204  {
205  return 0;
206  }
207 
208  uint
209  getVerticalOffset() const override
210  {
211  return 0;
212  }
213 
214  WidgetHook&
215  getCanvas() const override
216  {
217  return unConst(this)->display_;
218  }
219 
220  void
221  updatePosition() override
222  {
223  /* NOOP */
224  }
225 
226 
227  public:
228  DormantClip(WidgetHook& displayAnchor)
229  : ClipData{}
230  , display_{displayAnchor}
231  , clipName_{}
232  { }
233 
235  DormantClip(ClipData&& existing)
236  : ClipData{std::move (existing)}
237  , display_{existing.getCanvas()}
238  , clipName_{existing.getClipName()}
239  { }
240  };
241 
242 
244  : public HookedWidget
245  , public ClipData
246  {
247 
248  /* === Interface ClipDelegate === */
249 
250  Appearance
251  currentAppearance() const override
252  {
254  return Appearance::COMPACT;
255  }
256 
257  Appearance
258  changeAppearance (Appearance desired) override
259  {
261  return currentAppearance();
262  }
263 
264  cuString
265  getClipName() const override
266  {
267  return widget::ElementBoxWidget::getName();
268  }
269 
270  void
271  setClipName(cuString& newName) override
272  {
273  widget::ElementBoxWidget::setName (newName);
274  }
275 
276  uint
277  getVerticalOffset() const override
278  {
279  return defaultOffsetY;
280  }
281 
282  uint
283  calcRequiredHeight() const override
284  {
285  return this->get_height();
286  }
287 
288  WidgetHook&
289  getCanvas() const override
290  {
291  return HookedWidget::getCanvas();
292  }
293 
294  void
295  updatePosition() override
296  {
297  WidgetHook::Pos nominalPos = establishHookPoint(nullptr);
298  this->moveTo (nominalPos.x, nominalPos.y);
299  establishHorizontalExtension();
300  }
301 
302  /* ==== Size and Layout handling ==== */
303 
307  int
309  {
310  int hsiz = getCanvas().getMetric().translateTimeToPixels (accessDuration());
311  return hsiz;
312  }
313 
314 
315  public:
316  ClipWidget(WidgetHook& displayAnchor, TimeSpan const& timings)
317  : HookedWidget{displayAnchor.hookedAt(timings, defaultOffsetY), widget::Kind::CONTENT
321  [this]() { return establishHorizontalExtension(); }
322  )}
323  , ClipData{timings}
324  {
325  show_all();
326  }
327 
329  ClipWidget(ClipData&& existing, WidgetHook* newView =nullptr)
330  : HookedWidget{existing.establishHookPoint(newView) , widget::Kind::CONTENT
332  , widget::name(existing.getClipName())
334  [this]() { return establishHorizontalExtension(); }
335  )}
336  , ClipData{std::move (existing)}
337  {
338  show_all();
339  }
340  };
341 
342 
343 
344  enum Mode { HIDDEN, SUMMARY, INDIVIDUAL };
345 
346  inline Mode
347  classifyAppearance (ClipDelegate::Appearance appearance)
348  {
349  return appearance < ClipDelegate::SYMBOLIC? HIDDEN
350  : appearance < ClipDelegate::ABRIDGED? SUMMARY
351  : INDIVIDUAL;
352  }
353 
354 
356  inline bool
357  canShow (Time start)
358  {
359  return start != Time::NEVER;
360  }
361 
362  inline bool
363  canRepresentAsClip (PDelegate& existing, optional<TimeSpan> const& timing)
364  {
365  return (existing and canShow(existing->accessStartTime()))
366  or (not existing and timing and canShow(timing->start())) ;
367  }
368 
369 
375  inline ClipDelegate*
376  buildDelegateFor (Mode newMode, PDelegate& existingDelegate, WidgetHook* newView, optional<TimeSpan> const& timing)
377  {
378  if (existingDelegate)
379  { // flip existing delegate to another instance for newMode...
380  REQUIRE (INSTANCEOF (ClipData, existingDelegate.get()));
381  ClipData& clipData = static_cast<ClipData&> (*existingDelegate);
382 
383  switch (newMode)
384  {
385  case HIDDEN:
386  return new DormantClip (std::move (clipData));
387  case INDIVIDUAL:
388  return new ClipWidget (std::move (clipData), newView);
389  case SUMMARY:
390  UNIMPLEMENTED ("Summary/Overview presentation style");
391  }
392  }
393  else
394  { // First time: build new delegate from scratch
395  REQUIRE (newMode==HIDDEN or (timing and canShow (timing->start())));
396  REQUIRE (newView);
397 
398  switch (newMode)
399  {
400  case HIDDEN:
401  return new DormantClip{*newView};
402  case INDIVIDUAL:
403  return new ClipWidget{*newView, *timing};
404  case SUMMARY:
405  UNIMPLEMENTED ("Summary/Overview presentation style");
406  }
407  }
408  NOTREACHED("unsupported clip display mode.");
409  }
410  }//(End)clip appearance details.
411 
412 
413 
414  /* === Appearance Style state transitions === */
415 
417  ClipDelegate::selectAppearance (PDelegate& existing, Appearance desired, WidgetHook* newView, optional<TimeSpan> const& timing)
418  {
419  REQUIRE (existing or newView, "need either an existing delegate or also a new View/Canvas");
420 
421  Appearance current = existing? existing->currentAppearance()
422  : Appearance::PENDING;
423  if (not canRepresentAsClip (existing, timing))
424  desired = Appearance::PENDING;
425  // classify all possible appearances into three base presentation modes
426  Mode curMode = classifyAppearance (current);
427  Mode newMode = classifyAppearance (desired);
428 
429  if (newView or newMode != curMode)
430  { // need to switch the clip delegate
431  PDelegate newState (buildDelegateFor (newMode, existing, newView, timing));
432  swap (existing, newState);
433  }
434  ENSURE (existing);
435  return existing->changeAppearance (desired);
436  // fine-tune appearance style within limits of the mode established
437  }
438 
439 
440  WidgetHook::Pos
442  {
443  if (not newView)
444  newView = & getCanvas();
445  return newView->hookedAt (Time{accessStartTime()}, defaultOffsetY);
446  }
447 
448 
449  Gtk::Widget&
451  {
452  if (manager and manager->currentAppearance() >= ClipDelegate::ABRIDGED)
453  return static_cast<ClipWidget&> (*manager);
454 
455  else
456  throw error::State (_Fmt{"Attempt to access the Widget for clip('%s') in presentation state %d. "
457  "This implies an error in the signal wiring logic and state handling."}
458  % string{manager? manager->getClipName() : "<not initialised>"}
459  % int {manager? manager->currentAppearance() : -1}
460  ,LERR_(UIWIRING)
461  );
462  }
463 
464 
465 
466 }}// namespace stage::timeline
static Appearance selectAppearance(PDelegate &existing, Appearance desired=PENDING, WidgetHook *newView=nullptr, optional< TimeSpan > const &timing=nullopt)
request to change the clip delegate&#39;s appearance style, if possible.
a mutable time value, behaving like a plain number, allowing copy and re-accessing ...
Definition: timevalue.hpp:241
static const string defaultName
placeholder name – typically overridden from the model
bool canShow(Time start)
special convention to suppress a clip with start time == Time::NEVER
Appearance currentAppearance() const override
presentation mode and style currently employed
ClipDelegate * buildDelegateFor(Mode newMode, PDelegate &existingDelegate, WidgetHook *newView, optional< TimeSpan > const &timing)
Appearance changeAppearance(Appearance desired) override
alter appearance style, to the degree possible for this delegate.
Appearance currentAppearance() const override
presentation mode and style currently employed
A Clip not directly mapped into presentation, yet present as entity within the timeline framework...
#define INSTANCEOF(CLASS, EXPR)
shortcut for subclass test, intended for assertions only.
Definition: util.hpp:492
Types marked with this mix-in may be moved but not copied.
Definition: nocopy.hpp:58
A widget attached onto a display canvas or similar central presentation context.
Front-end for printf-style string template interpolation.
Widget to render an ID label with associated icon.
virtual Appearance currentAppearance() const =0
presentation mode and style currently employed
Appearance
desired appearance style for the clip
Appearance changeAppearance(Appearance) override
alter appearance style, to the degree possible for this delegate.
A front-end for using printf-style formatting.
static const Duration NIL
constant to indicate "no duration"
Definition: timevalue.hpp:515
Lumiera&#39;s internal time value datatype.
Definition: timevalue.hpp:308
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:199
WidgetHook::Pos establishHookPoint(WidgetHook *newView)
(re)establish current canvas attachment coordinates, thereby possibly switching to a new canvas imple...
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...
cuString getClipName() const override
human readable rendering of the clip&#39;s name or identity
virtual ~ClipDelegate()
this is an interface
This widget provides the concrete rendering of a clip-like entity.
static const Time NEVER
border condition marker value. NEVER >= any time value
Definition: timevalue.hpp:323
represents moving (or still) image data
ClipWidget(ClipData &&existing, WidgetHook *newView=nullptr)
state switch ctor
static Gtk::Widget & expect_and_expose_Widget(PDelegate &manager)
Wrapper to safely expose the actual clip implementation widget.
int establishHorizontalExtension()
use underlying canvas metric to derive a size constraint, taking into account the duration of the cli...
static const int defaultOffsetY
vertical offset below the track start
A time interval anchored at a specific point in time.
Definition: timevalue.hpp:582
cuString getClipName() const override
human readable rendering of the clip&#39;s name or identity
Widget serves to represent a piece of content (Clip)
A set of basic GTK includes for the UI.
ElementBoxWidget::Config::Qualifier constrained(SizeGetter widthConstraint)
switch in to size-constrained layout mode.
ElementBoxWidget::Config::Qualifier name(string id)
define the name-ID displayed in the caption
uint calcRequiredHeight() const override
This is a mere data record without actual presentation, and thus can not occupy any screen extension...