* restructure the widgets used to implement ElementBox * inject a Gtk::EventBox top-level base type to capture all Gdk-Events * push the Gtk::Frame one level down (TODO: API for managing children) With these changes * dragging of Clips in the timeline works as expected * size constraints are observed precisely * all warnings and assertion failures from GTK disappeared Thus we can conclude that the solution approach for size constrained widgets was successful and this challenging problem is solved.
362 lines
13 KiB
C++
362 lines
13 KiB
C++
/*
|
|
DummySessionConnection - scaffolding placeholder to drive the GUI-Session connection
|
|
|
|
Copyright (C) Lumiera.org
|
|
2016, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of
|
|
the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
* *****************************************************/
|
|
|
|
|
|
/** @file dummy-session-connection.cpp
|
|
** Implementation details to build some scaffolding for UI<-->Session integration.
|
|
** In this translation unit, actually a singleton will be instantiated, whenever
|
|
** some other part of the application (or some unit test) needs backing by a faked
|
|
** session with...
|
|
** - some session content
|
|
** - commands to be invoked
|
|
**
|
|
** @todo WIP as of 10/2018 ///////////////////////TICKET #1042
|
|
**
|
|
** @see [corresponding UI](\ref stage::dialog::TestControl)
|
|
**
|
|
*/
|
|
|
|
#include "lib/util.hpp"
|
|
//#include "lib/symbol.hpp"
|
|
//#include "include/logging.h"
|
|
#include "steam/mobject/session/dummy-session-connection.hpp"
|
|
#include "steam/mobject/session/root.hpp"
|
|
#include "steam/control/command-def.hpp"
|
|
#include "include/ui-protocol.hpp"
|
|
#include "include/gui-notification-facade.h"
|
|
#include "lib/diff/tree-diff-application.hpp"
|
|
#include "lib/diff/mutation-message.hpp"
|
|
#include "lib/diff/gen-node.hpp"
|
|
#include "lib/time/timevalue.hpp"
|
|
//#include "lib/idi/entry-id.hpp"
|
|
#include "lib/format-string.hpp"
|
|
#include "lib/format-cout.hpp"
|
|
//#include "lib/symbol.hpp"
|
|
#include "lib/util.hpp"
|
|
|
|
#include <string>
|
|
//#include <map>
|
|
|
|
using lib::diff::MutationMessage;
|
|
using lib::diff::GenNode;
|
|
using lib::diff::MakeRec;
|
|
using lib::diff::Ref;
|
|
using lib::idi::RandID;
|
|
using lib::time::Time;
|
|
using lib::time::FSecs;
|
|
using lib::time::Duration;
|
|
using lib::time::TimeSpan;
|
|
//using util::cStr;
|
|
using util::_Fmt;
|
|
using std::string;
|
|
|
|
#include <string>
|
|
|
|
//using std::map;
|
|
using std::string;
|
|
|
|
using util::contains;
|
|
using util::isnil;
|
|
|
|
namespace steam {
|
|
namespace mobject {
|
|
namespace session {
|
|
|
|
namespace { //Implementation details....
|
|
|
|
/** @note timeline(toplevel) follows a special convention:
|
|
* initial population already includes track fork (root).
|
|
*/
|
|
GenNode
|
|
emptyTimeline (string baseID, RandID const& forkRootID)
|
|
{
|
|
return MakeRec()
|
|
.set(MakeRec()
|
|
.type (string{stage::TYPE_Fork})
|
|
.genNode(forkRootID)
|
|
)
|
|
.genNode(baseID);
|
|
}
|
|
|
|
GenNode
|
|
emptyTrack (string trackID)
|
|
{
|
|
return MakeRec()
|
|
.type (string{stage::TYPE_Fork})
|
|
.genNode(trackID);
|
|
}
|
|
|
|
GenNode
|
|
ruler()
|
|
{
|
|
return MakeRec()
|
|
.type (string{stage::TYPE_Ruler})
|
|
.genNode("Ruler");
|
|
}
|
|
|
|
GenNode
|
|
clip (string clipID, TimeSpan timings)
|
|
{
|
|
return MakeRec()
|
|
.type (string{stage::TYPE_Clip})
|
|
.set(string{stage::ATTR_timing}, timings)
|
|
.genNode(clipID);
|
|
}
|
|
GenNode
|
|
clip (string clipID, Time start, Duration dur = Duration{FSecs{1}})
|
|
{
|
|
return clip (clipID, TimeSpan{start,dur});
|
|
}
|
|
|
|
/** fabricate an attribute node based on the
|
|
* human-readable part of the given elemen's ID */
|
|
GenNode
|
|
makeName (GenNode const& elm)
|
|
{
|
|
return GenNode{string{stage::ATTR_name}, elm.idi.getSym() };
|
|
}
|
|
|
|
/** define the (optional) timings for a clip */
|
|
GenNode
|
|
defineTiming (Time start, Duration dur = Duration{FSecs{1}})
|
|
{
|
|
return GenNode{string{stage::ATTR_timing}, TimeSpan{start,dur} };
|
|
}
|
|
} //(End)Implementation details....
|
|
|
|
|
|
|
|
|
|
/** storage for the Singleton accessor */
|
|
lib::Depend<DummySessionConnection> DummySessionConnection::instance;
|
|
|
|
|
|
DummySessionConnection::DummySessionConnection()
|
|
{ }
|
|
|
|
DummySessionConnection::~DummySessionConnection() { }
|
|
|
|
|
|
|
|
/**
|
|
* Build a population diff message to describe a specific session structure to add
|
|
*/
|
|
MutationMessage
|
|
DummySessionConnection::fabricateSeq1 (string baseID)
|
|
{
|
|
const RandID forkRootID{stage::ATTR_fork};
|
|
const GenNode timeline = emptyTimeline (baseID, forkRootID);
|
|
const GenNode rootTrackName = GenNode{string{stage::ATTR_name}, "Track-"+baseID};
|
|
const GenNode forkRoot = MakeRec().genNode(forkRootID);
|
|
const GenNode clip1 = clip ("Clip-1", Time::ZERO);
|
|
const GenNode clip2 = clip ("Clip-2", Time::NEVER);
|
|
|
|
return MutationMessage{after(Ref::END)
|
|
, ins (timeline)
|
|
, mut (timeline)
|
|
, mut (forkRoot)
|
|
, set (rootTrackName)
|
|
, ins (clip1)
|
|
, ins (clip2)
|
|
, mut (clip1)
|
|
, ins (makeName(clip1))
|
|
, emu (clip1)
|
|
, mut (clip2)
|
|
, ins (makeName(clip2))
|
|
, ins (defineTiming(Time{FSecs{5}}, Duration{FSecs{4}}))
|
|
, emu (clip2)
|
|
, emu (forkRoot)
|
|
, emu (timeline)
|
|
};
|
|
}
|
|
|
|
|
|
/**
|
|
* Build another population diff message for a way more contrived timeline structure
|
|
*/
|
|
MutationMessage
|
|
DummySessionConnection::fabricateSeq2 (string baseID)
|
|
{
|
|
const RandID forkRootID{stage::ATTR_fork};
|
|
const GenNode timeline = emptyTimeline (baseID, forkRootID);
|
|
const GenNode rootTrackName = GenNode{string{stage::ATTR_name}, "Fork-Root"};
|
|
const GenNode forkRoot = MakeRec().genNode(forkRootID);
|
|
const GenNode track1 = emptyTrack ("Track-1");
|
|
const GenNode track2 = emptyTrack ("Track-2");
|
|
const GenNode track21 = emptyTrack ("Track-21");
|
|
const GenNode track22 = emptyTrack ("Track-22");
|
|
const GenNode track221 = emptyTrack ("Track-221");
|
|
const GenNode track222 = emptyTrack ("Track-222");
|
|
const GenNode timeRuler = ruler();
|
|
const GenNode scopeRuler0 = ruler();
|
|
const GenNode scopeRuler2 = ruler();
|
|
const GenNode scopeRuler22 = ruler();
|
|
const GenNode scopeRuler221 = ruler();
|
|
|
|
return MutationMessage{after(Ref::END)
|
|
, ins (timeline)
|
|
, mut (timeline)
|
|
, mut (forkRoot)
|
|
, ins (rootTrackName)
|
|
, ins (track1)
|
|
, ins (track2)
|
|
, ins (timeRuler)
|
|
, ins (scopeRuler0)
|
|
, mut (track1)
|
|
, ins (makeName(track1))
|
|
, emu (track1)
|
|
, mut (track2)
|
|
, ins (makeName(track2))
|
|
, ins (track21)
|
|
, ins (track22)
|
|
, ins (scopeRuler2)
|
|
, mut (track21)
|
|
, ins (makeName(track21))
|
|
, emu (track21)
|
|
, mut (track22)
|
|
, ins (makeName(track22))
|
|
, ins (track221)
|
|
, ins (track222)
|
|
, ins (scopeRuler22)
|
|
, mut (track221)
|
|
, ins (makeName(track221))
|
|
, ins (scopeRuler221)
|
|
, emu (track221)
|
|
, mut (track222)
|
|
, ins (makeName(track222))
|
|
, emu (track222)
|
|
, emu (track22)
|
|
, emu (track2)
|
|
, emu (forkRoot)
|
|
, emu (timeline)
|
|
};
|
|
}
|
|
|
|
|
|
/** @todo build an internal "shadow data structure" and apply all diffs there as well...*/
|
|
void
|
|
DummySessionConnection::applyCopy (MutationMessage const& diff)
|
|
{
|
|
TODO ("build internal diagnostic data structure, apply a copy of the message");
|
|
}
|
|
|
|
|
|
}}// namespace steam::mobject::session
|
|
|
|
namespace cmd {
|
|
|
|
using lib::hash::LuidH;
|
|
using stage::ID;
|
|
using stage::NOTE_INFO;
|
|
using stage::NOTE_WARN;
|
|
using stage::NOTE_ERROR;
|
|
// using stage::NotifyLevel;
|
|
// using stage::MARK_expand;
|
|
using stage::GuiNotification;
|
|
// using util::isYes;
|
|
|
|
namespace session = steam::mobject::session;
|
|
|
|
using DummySess = session::DummySessionConnection;
|
|
|
|
|
|
/* ============ dedicated Fake-Commands ============ */
|
|
|
|
/** Populate the Timeline in the UI with a typical simple Dummy sequence.
|
|
* This Steam-Layer command script fabricates a faked "population diff", which not
|
|
* corresponds to any existing session data structure, but looks as if emanated while
|
|
* loading current session state.
|
|
* - one single Timeline
|
|
* - just the root track
|
|
* - two clips placed on that track
|
|
* @todo use this to establish basic Timeline display in the UI //////////////////TICKET #1014 : produce Dummy content to populate timeline
|
|
*/
|
|
COMMAND_DEFINITION (test_fake_injectSequence_1)
|
|
{
|
|
def.operation ([](string dummyID)
|
|
{
|
|
string message{_Fmt{"fabricate Sequence_1 (dummyID='%s')"} % dummyID};
|
|
GuiNotification::facade().displayInfo (NOTE_INFO, message);
|
|
auto popuDiff = DummySess::instance().fabricateSeq1 (dummyID);
|
|
DummySess::instance().applyCopy (popuDiff);
|
|
auto rootID = session::Root::getID();
|
|
GuiNotification::facade().mutate (rootID, move(popuDiff));
|
|
})
|
|
.captureUndo ([](string dummyID) -> string
|
|
{
|
|
return _Fmt{"fabricateSequence_1('%s')"} % dummyID;
|
|
})
|
|
.undoOperation ([](string, string memento)
|
|
{
|
|
GuiNotification::facade().displayInfo (NOTE_WARN, "can not UNDO Dummy-Action: "+memento);
|
|
});
|
|
};
|
|
|
|
|
|
/** Populate the Timeline in the UI with a rather complex Dummy sequence.
|
|
* This command script fabricates a faked convoluted "population diff"...
|
|
* @todo use this to enact a complex layout structure in the Timeline-UI //////////////////TICKET #1014 : produce Dummy content to populate timeline
|
|
*/
|
|
COMMAND_DEFINITION (test_fake_injectSequence_2)
|
|
{
|
|
def.operation ([](string dummyID)
|
|
{
|
|
string message{_Fmt{"fabricate Sequence_2 (dummyID='%s')"} % dummyID};
|
|
GuiNotification::facade().displayInfo (NOTE_INFO, message);
|
|
auto popuDiff = DummySess::instance().fabricateSeq2 (dummyID);
|
|
DummySess::instance().applyCopy (popuDiff);
|
|
auto rootID = session::Root::getID();
|
|
GuiNotification::facade().mutate (rootID, move(popuDiff));
|
|
})
|
|
.captureUndo ([](string dummyID) -> string
|
|
{
|
|
return _Fmt{"fabricateSequence_2('%s')"} % dummyID;
|
|
})
|
|
.undoOperation ([](string, string memento)
|
|
{
|
|
GuiNotification::facade().displayInfo (NOTE_WARN, "can not UNDO Dummy-Action: "+memento);
|
|
});
|
|
};
|
|
|
|
|
|
/** Template for dummy-code....
|
|
* @todo use this to enact a complex layout structure in the Timeline-UI //////////////////TICKET #1042 : further the DummySessionConnection
|
|
*/
|
|
COMMAND_DEFINITION (test_fake_blubb)
|
|
{
|
|
def.operation ([](string dummyID)
|
|
{
|
|
string message{_Fmt{"fabricate gulp (dummyID='%s')"} % dummyID};
|
|
GuiNotification::facade().displayInfo (NOTE_INFO, message);
|
|
})
|
|
.captureUndo ([](string dummyID) -> string
|
|
{
|
|
return _Fmt{"fabricateGulp('%s')"} % dummyID;
|
|
})
|
|
.undoOperation ([](string, string memento)
|
|
{
|
|
GuiNotification::facade().displayInfo (NOTE_WARN, "can not UNDO Dummy-Action: "+memento);
|
|
});
|
|
};
|
|
|
|
|
|
}} // namespace steam::mobject::session
|