decide upon the actual mechanics of command binding and invocation

This commit is contained in:
Fischlurch 2015-11-28 08:15:32 +01:00
parent 42cec6d604
commit 2704ad4512
8 changed files with 188 additions and 46 deletions

View file

@ -22,10 +22,18 @@
/** @file invocation-trail.hpp /** @file invocation-trail.hpp
** Abstraction: a tangible element of the User Interface. ** A command in preparation of being issued from the UI.
** Any such element is connected to the UIBus... ** The actual persistent operations on the session model are defined
** as DSL scripts acting on the session interface, and configured as a
** _command prototype_. Typically these need to be enriched with at least
** the actual subject to invoke this command on; many commands require
** additional parameters, e.g. some time or colour value. These actual
** invocation parameters need to be picked up from UI elements, and the
** process of preparing and outfitting a generic command with these
** actual values is tracked by an [InvocationTrail] handle. When
** ready, finally this handle can be issued on any [BusTerm].
** **
** @todo as of 1/2015 this is complete WIP-WIP-WIP ** @todo as of 11/2015 this is complete WIP-WIP-WIP
** **
** @see ////TODO_test usage example ** @see ////TODO_test usage example
** **
@ -36,59 +44,61 @@
#define GUI_INTERACT_INVOCATION_TRAIL_H_ #define GUI_INTERACT_INVOCATION_TRAIL_H_
#include "lib/error.hpp" #include "proc/control/command.hpp"
#include "gui/ctrl/bus-term.hpp" #include "lib/diff/gen-node.hpp"
#include "lib/idi/entry-id.hpp"
//#include "lib/symbol.hpp"
//#include "lib/util.hpp"
#include <boost/noncopyable.hpp> #include <utility>
#include <string> #include <string>
namespace gui { namespace gui {
namespace interact { namespace interact {
// using lib::HashVal; using lib::diff::GenNode;
// using util::isnil; using lib::diff::Rec;
using std::string; using std::string;
/** /**
* Interface common to all UI elements of relevance for the Lumiera application. * A concrete command invocation in the state of preparation and argument binding.
* Any non-local and tangible interface interaction will at some point pass through * This value object is a tracking handle used within the UI to deal with establishing
* this foundation element, which forms the joint and attachment to the UI backbone, * a command context, maybe to present the command within a menu or to picking up
* which is the [UI-Bus][ui-bus.hpp]. Any tangible element acquires a distinct identity * actual invocation parameters from the context.
* and has to be formed starting from an already existing bus nexus. * @remarks typically you don't create an InvocationTrail from scratch; rather
* * you'll find it embedded into rules placed into a [InteractionStateManager].
* @todo write type comment... * The intention is to define it alongside with the command prototype.
*/ */
class InvocationTrail class InvocationTrail
: boost::noncopyable
{ {
protected: string cmdID_;
Tangible(EntryID identity, ctrl::BusTerm nexus)
: uiBus_(nexus.attach(identity))
{ }
public: public:
virtual ~Tangible(); ///< this is an interface InvocationTrail(proc::control::Command prototype)
: cmdID_(prototype.getID())
{ }
void reset(); GenNode bind (Rec&& cmdArgs) const
{
return GenNode(cmdID_, std::forward<Rec>(cmdArgs));
}
void slotExpand(); GenNode bang() const
void slotReveal(); {
return GenNode(cmdID_, FLAGS);
}
void noteMsg(); operator string() const
void noteErr(); {
void noteFlash(); return "InvocationTrail cmd(\""+cmdID_+"\"";
void noteMark(); }
protected:
virtual void doExpand() =0;
virtual void doReveal() =0;
private: private:
/** @todo unused as of 11/2015
* some additional instantiation metadata
* could be passed alongside with the invocation.
*/
static const int FLAGS = 42;
}; };

View file

@ -57,16 +57,33 @@ namespace model {
/** /**
* * Prepare a command or action for actual invocation, once the execution context
* @param id * has been established. The action is not executed right away, but it is now ready
* @return * and bound to the concrete arguments supplied with the [record][lib::diff::Rec].
* @param prototype handle to a command instantiation, to be readied for invocation
* @param arguments suitable tuple of values, to be used to outfit the prototype
*/ */
string void
fun (string& id) Tangible::prepareCommand (InvocationTrail const& prototype, Rec&& arguments)
{ {
return "x"+id; TODO ("invoke some hook for instrumentation?");
uiBus_.act (prototype.bind(std::forward<Rec>(arguments)));
} }
/**
* Actually trigger execution of an action or command.
* @param preparedAction handle pointing to a command definition,
* which needs to be outfitted with arguments and ready for invocation.
*/
void
Tangible::issueCommand (InvocationTrail const& preparedAction)
{
TODO ("invoke some hook for instrumentation?");
uiBus_.act (prototype.bang());
}
}} // namespace gui::model }} // namespace gui::model

View file

@ -38,6 +38,7 @@
#include "lib/error.hpp" #include "lib/error.hpp"
#include "gui/ctrl/bus-term.hpp" #include "gui/ctrl/bus-term.hpp"
#include "gui/interact/invocation-trail.hpp"
#include "lib/idi/entry-id.hpp" #include "lib/idi/entry-id.hpp"
//#include "lib/symbol.hpp" //#include "lib/symbol.hpp"
//#include "lib/util.hpp" //#include "lib/util.hpp"
@ -67,8 +68,13 @@ namespace model {
: boost::noncopyable : boost::noncopyable
{ {
protected: protected:
using interact::InvocationTrail;
using lib::diff::GenNode;
using lib::diff::Rec;
ctrl::BusTerm uiBus_; ctrl::BusTerm uiBus_;
Tangible(EntryID identity, ctrl::BusTerm nexus) Tangible(EntryID identity, ctrl::BusTerm nexus)
: uiBus_(nexus.attach(identity)) : uiBus_(nexus.attach(identity))
{ } { }
@ -78,6 +84,9 @@ namespace model {
void reset(); void reset();
void prepareCommand (InvocationTrail const& prototype, Rec&& arguments);
void issueCommand (InvocationTrail const& preparedAction);
void slotExpand(); void slotExpand();
void slotReveal(); void slotReveal();

View file

@ -178,6 +178,8 @@ namespace control {
////////////////////TODO the real access operations (e.g. for serialising) go here ////////////////////TODO the real access operations (e.g. for serialising) go here
/////////////////////////////////////////////////////////////TICKET #798 : we need to pick up arguments from a lib::diff::Record.
ostream& ostream&
dump (ostream& output) const dump (ostream& output) const

View file

@ -326,6 +326,17 @@ namespace control {
} }
Symbol
Command::getID() const
{
Symbol id = CommandRegistry::instance().findDefinition (*this);
if (!id)
throw error::State("Encountered a NIL command handle while expecting a bound one."
,error::LUMIERA_ERROR_BOTTOM_VALUE);
return id;
}
/** diagnostics: shows the commandID, if any, /** diagnostics: shows the commandID, if any,

View file

@ -133,6 +133,9 @@ namespace control {
template<typename TYPES> template<typename TYPES>
Command& bindArg (Tuple<TYPES> const&); Command& bindArg (Tuple<TYPES> const&);
/////////////////////////////////////////////////////////////TICKET #798 : we need a second overload to take the arguments as lib::diff::Record.
///////////////////////////////////////////////////////////// : this needs to be built into the ParamAccessor within Closure (command-closure.hpp)
ExecResult operator() () ; ExecResult operator() () ;
ExecResult undo () ; ExecResult undo () ;
@ -171,6 +174,8 @@ namespace control {
void duplicate_detected (Symbol) const; void duplicate_detected (Symbol) const;
Symbol getID() const;
operator string() const; operator string() const;
friend bool operator== (Command const&, Command const&); friend bool operator== (Command const&, Command const&);
friend bool operator< (Command const&, Command const&); friend bool operator< (Command const&, Command const&);

View file

@ -8371,7 +8371,7 @@ The UI-Bus is a ''Mediator'' -- impersonating the role of the //Model// and the
The ~MVC-Pattern as such is fine, and probably the best we know for construction of user interfaces. But it doesn't scale well towards the integration into a larger and more structured system. There is a tension between the Controller in the UI and other parts of an application, which as well need to be //in control.// And, even more important, there is a tension between the demands of UI elements for support by a model, and the demands to be placed on a core domain model of a large scale application. This tension is resolved by enacting these roles while transforming the requests and demands into //Messages.//</pre> The ~MVC-Pattern as such is fine, and probably the best we know for construction of user interfaces. But it doesn't scale well towards the integration into a larger and more structured system. There is a tension between the Controller in the UI and other parts of an application, which as well need to be //in control.// And, even more important, there is a tension between the demands of UI elements for support by a model, and the demands to be placed on a core domain model of a large scale application. This tension is resolved by enacting these roles while transforming the requests and demands into //Messages.//</pre>
</div> </div>
<div title="UI-Element" creator="Ichthyostega" modifier="Ichthyostega" created="201511210307" modified="201511272242" tags="GuiPattern design draft decision" changecount="16"> <div title="UI-Element" creator="Ichthyostega" modifier="Ichthyostega" created="201511210307" modified="201511280638" tags="GuiPattern design draft decision" changecount="17">
<pre>While our UI widgets are implemented the standard way as proposed by GTKmm, some key elements -- which are especially relevant for the anatomy and mechanics of the interface -- are made to conform to a common interface and behaviour protocol. {{red{WIP 11/15 work out what this protocol is all about}}}. #975 <pre>While our UI widgets are implemented the standard way as proposed by GTKmm, some key elements -- which are especially relevant for the anatomy and mechanics of the interface -- are made to conform to a common interface and behaviour protocol. {{red{WIP 11/15 work out what this protocol is all about}}}. #975
As a starting point, we know As a starting point, we know
* there is a backbone structure known as the UI-Bus * there is a backbone structure known as the UI-Bus
@ -8423,6 +8423,9 @@ We should note that these conventions of interchange lead to a recursive or ''se
While the above definitions might seem more or less obvious and reasonable, there is one tiny detail, which -- on second thought -- unfolds into a fundamental decision to be taken. The point in question is //how we refer to a command.// More specifically: is referring to a command something generic, or is it rather something left to the actual implementing widget? In the first case, a generic foundation element has to provide some framework to deal with command definitions, whereas in the second case just a protected slot to pass on invocations from derived classes would be sufficient. This is a question of fundamental importance; subsidiarity has its merits, so once we forgo the opportunity to build from a generic pattern, local patterns will take over, while similarities and symmetries have to grow and wait to be discovered sometimes, if at all. This might actually not be a problem -- yet if you know Lumiera, you know that we tend to look at existing practice and draw fundamental conclusions, prior to acting. While the above definitions might seem more or less obvious and reasonable, there is one tiny detail, which -- on second thought -- unfolds into a fundamental decision to be taken. The point in question is //how we refer to a command.// More specifically: is referring to a command something generic, or is it rather something left to the actual implementing widget? In the first case, a generic foundation element has to provide some framework to deal with command definitions, whereas in the second case just a protected slot to pass on invocations from derived classes would be sufficient. This is a question of fundamental importance; subsidiarity has its merits, so once we forgo the opportunity to build from a generic pattern, local patterns will take over, while similarities and symmetries have to grow and wait to be discovered sometimes, if at all. This might actually not be a problem -- yet if you know Lumiera, you know that we tend to look at existing practice and draw fundamental conclusions, prior to acting.
&amp;rarr; InteractionControl &amp;rarr; InteractionControl
&amp;rarr; GuiCommandBinding &amp;rarr; GuiCommandBinding
!!!actual implementation of command invocation
The {{{InvocationTrail}}} of an command is actually just a tag object, wrapping the command definition ID. In a first step, when the arguments can be closed, a {{{Record}}} with suitable bindings is created and wrapped into a {{{GenNode}}} corresponding to the command. This compound is sent over the UI-Bus, marking the command ready for execution. From this point on, just the {{{InvocationTrail}}} is passed to enable the trigger sites. When it is triggered, just a &quot;bang!&quot; message with the command ID is sent over the bus. An command instance can be re-used, as long as it is structurally the same as before, and as log as there is no possible collision with a similar, but differently parametrised instantiation process.
</pre> </pre>
</div> </div>
<div title="ViewConnection" modifier="Ichthyostega" created="201105221854" modified="201501091154" tags="def Model SessionLogic" changecount="3"> <div title="ViewConnection" modifier="Ichthyostega" created="201105221854" modified="201501091154" tags="def Model SessionLogic" changecount="3">

View file

@ -247,7 +247,7 @@
<node CREATED="1448659283641" ID="ID_209203268" MODIFIED="1448659290276" TEXT="letztlich ein String"/> <node CREATED="1448659283641" ID="ID_209203268" MODIFIED="1448659290276" TEXT="letztlich ein String"/>
<node CREATED="1448659347728" ID="ID_508578010" MODIFIED="1448659355835" TEXT="Konstante bei der Command-Funktion"/> <node CREATED="1448659347728" ID="ID_508578010" MODIFIED="1448659355835" TEXT="Konstante bei der Command-Funktion"/>
<node CREATED="1448659334058" ID="ID_683316035" MODIFIED="1448659340357" TEXT="Makro + Namenskonvention"/> <node CREATED="1448659334058" ID="ID_683316035" MODIFIED="1448659340357" TEXT="Makro + Namenskonvention"/>
<node CREATED="1448683561882" ID="ID_839961131" MODIFIED="1448683880173"> <node CREATED="1448683561882" ID="ID_839961131" MODIFIED="1448691299107">
<richcontent TYPE="NODE"><html> <richcontent TYPE="NODE"><html>
<head> <head>
@ -260,6 +260,7 @@
</html> </html>
</richcontent> </richcontent>
<arrowlink COLOR="#81759f" DESTINATION="ID_1679641405" ENDARROW="Default" ENDINCLINATION="604;-108;" ID="Arrow_ID_1210299906" STARTARROW="None" STARTINCLINATION="702;0;"/> <arrowlink COLOR="#81759f" DESTINATION="ID_1679641405" ENDARROW="Default" ENDINCLINATION="604;-108;" ID="Arrow_ID_1210299906" STARTARROW="None" STARTINCLINATION="702;0;"/>
<arrowlink COLOR="#aaa9c1" DESTINATION="ID_1193936433" ENDARROW="Default" ENDINCLINATION="392;0;" ID="Arrow_ID_1338946264" STARTARROW="Default" STARTINCLINATION="392;0;"/>
<icon BUILTIN="button_ok"/> <icon BUILTIN="button_ok"/>
</node> </node>
</node> </node>
@ -279,7 +280,7 @@
<node CREATED="1448683674443" ID="ID_1931753413" MODIFIED="1448683684206" TEXT="Einheitlichkeit gef&#xe4;hrdet"/> <node CREATED="1448683674443" ID="ID_1931753413" MODIFIED="1448683684206" TEXT="Einheitlichkeit gef&#xe4;hrdet"/>
</node> </node>
</node> </node>
<node CREATED="1448683454504" HGAP="26" ID="ID_510876193" MODIFIED="1448683468155" TEXT="API generisch"> <node CREATED="1448683454504" HGAP="42" ID="ID_510876193" MODIFIED="1448691201766" TEXT="API generisch" VSHIFT="-1">
<icon BUILTIN="button_ok"/> <icon BUILTIN="button_ok"/>
<node CREATED="1448683636672" ID="ID_1485949406" MODIFIED="1448683647815" TEXT="verhindert Wildwuchs"/> <node CREATED="1448683636672" ID="ID_1485949406" MODIFIED="1448683647815" TEXT="verhindert Wildwuchs"/>
<node CREATED="1448683475853" ID="ID_24599710" MODIFIED="1448683492586"> <node CREATED="1448683475853" ID="ID_24599710" MODIFIED="1448683492586">
@ -304,6 +305,90 @@
<node CREATED="1448683525822" ID="ID_1395068730" MODIFIED="1448683529258" TEXT="Gesten"/> <node CREATED="1448683525822" ID="ID_1395068730" MODIFIED="1448683529258" TEXT="Gesten"/>
</node> </node>
</node> </node>
<node CREATED="1448691191042" HGAP="35" ID="ID_1710578352" MODIFIED="1448691205990" TEXT="Lebenszyklus" VSHIFT="-1">
<font NAME="SansSerif" SIZE="13"/>
<node CREATED="1448691210544" ID="ID_242337741" MODIFIED="1448691218906" TEXT="Command-Skript: im Code"/>
<node CREATED="1448691219614" ID="ID_992447056" MODIFIED="1448691241647" TEXT="Bildungs-Regeln: ebenda">
<node CREATED="1448691243259" ID="ID_692910736" MODIFIED="1448691256433" TEXT="Frage: wie injizieren">
<icon BUILTIN="help"/>
</node>
</node>
<node CREATED="1448691264512" ID="ID_1193936433" MODIFIED="1448691288828" TEXT="InvocationTrail erstellen">
<linktarget COLOR="#aaa9c1" DESTINATION="ID_1193936433" ENDARROW="Default" ENDINCLINATION="392;0;" ID="Arrow_ID_1338946264" SOURCE="ID_839961131" STARTARROW="Default" STARTINCLINATION="392;0;"/>
</node>
<node CREATED="1448691315849" ID="ID_1198560195" MODIFIED="1448691320189" TEXT="Bindugs-Regel">
<node CREATED="1448691343406" ID="ID_642235825" MODIFIED="1448691350558" TEXT="funktor"/>
<node CREATED="1448691324304" ID="ID_1103199702" MODIFIED="1448691338522" TEXT="definiert Bezugs-Quellen"/>
<node CREATED="1448691355340" ID="ID_724965495" MODIFIED="1448691465633" TEXT="liefert Record&lt;GenNode&gt;"/>
</node>
<node CREATED="1448691865257" ID="ID_1343859473" MODIFIED="1448691874244" TEXT="Command-Binding wird gesendet">
<node CREATED="1448691954533" ID="ID_966334383" MODIFIED="1448691960295" TEXT="Performance">
<icon BUILTIN="help"/>
</node>
<node CREATED="1448691962068" ID="ID_1045203424" MODIFIED="1448691974575" TEXT="nicht &quot;auf Verdacht&quot; senden">
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1448692020868" ID="ID_705436290" MODIFIED="1448692192462" TEXT="Begr&#xfc;ndung: kein Kopieren der Argumente">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
die Alternative w&#228;re, den Record mit allen Argumenten in den InvocationTrail zu packen.
</p>
<p>
Dann w&#252;rden wir diesen aber weiter versenden, um Aktivierungen zuzustellen.
</p>
<p>
Das w&#252;rde bedeuten, die Argumente x-fach zu kopieren (oder mich zu einem ref-counting-Mechanismus zwingen)
</p>
<p>
</p>
<p>
Daher ist es besser, <i>einmal</i>, wenn die Argumente bekannt werden, diese zum Prototypen zu schicken
</p>
</body>
</html>
</richcontent>
</node>
</node>
<node CREATED="1448691875184" ID="ID_642935499" MODIFIED="1448691904352" TEXT="f&#xfc;r die Invocation gen&#xfc;gt dann die reine ID"/>
<node CREATED="1448692196213" ID="ID_727542699" MODIFIED="1448692339433">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
Wichtig <font color="#e10409">(offen)</font>: Instanz-Management
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1448692240015" ID="ID_58254695" MODIFIED="1448692245826" TEXT="es geht um Allokationen in Proc"/>
<node CREATED="1448692247478" ID="ID_1199512787" MODIFIED="1448692258689" TEXT="jede Instanz == Eintrag in der Command-Registry"/>
<node CREATED="1448692287329" ID="ID_1099768193" MODIFIED="1448692317249">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
Regel: nur was sich parallel entwickeln kann,
</p>
<p>
mu&#223; auch geforkt werden
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="idea"/>
</node>
</node>
</node>
</node> </node>
</node> </node>
</node> </node>