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
** Abstraction: a tangible element of the User Interface.
** Any such element is connected to the UIBus...
** A command in preparation of being issued from the UI.
** 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
**
@ -36,59 +44,61 @@
#define GUI_INTERACT_INVOCATION_TRAIL_H_
#include "lib/error.hpp"
#include "gui/ctrl/bus-term.hpp"
#include "lib/idi/entry-id.hpp"
//#include "lib/symbol.hpp"
//#include "lib/util.hpp"
#include "proc/control/command.hpp"
#include "lib/diff/gen-node.hpp"
#include <boost/noncopyable.hpp>
#include <utility>
#include <string>
namespace gui {
namespace interact {
// using lib::HashVal;
// using util::isnil;
using lib::diff::GenNode;
using lib::diff::Rec;
using std::string;
/**
* Interface common to all UI elements of relevance for the Lumiera application.
* Any non-local and tangible interface interaction will at some point pass through
* this foundation element, which forms the joint and attachment to the UI backbone,
* which is the [UI-Bus][ui-bus.hpp]. Any tangible element acquires a distinct identity
* and has to be formed starting from an already existing bus nexus.
*
* @todo write type comment...
* A concrete command invocation in the state of preparation and argument binding.
* This value object is a tracking handle used within the UI to deal with establishing
* a command context, maybe to present the command within a menu or to picking up
* actual invocation parameters from the context.
* @remarks typically you don't create an InvocationTrail from scratch; rather
* you'll find it embedded into rules placed into a [InteractionStateManager].
* The intention is to define it alongside with the command prototype.
*/
class InvocationTrail
: boost::noncopyable
{
protected:
Tangible(EntryID identity, ctrl::BusTerm nexus)
: uiBus_(nexus.attach(identity))
{ }
string cmdID_;
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();
void slotReveal();
GenNode bang() const
{
return GenNode(cmdID_, FLAGS);
}
void noteMsg();
void noteErr();
void noteFlash();
void noteMark();
operator string() const
{
return "InvocationTrail cmd(\""+cmdID_+"\"";
}
protected:
virtual void doExpand() =0;
virtual void doReveal() =0;
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
* has been established. The action is not executed right away, but it is now ready
* 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
*/
void
Tangible::prepareCommand (InvocationTrail const& prototype, Rec&& arguments)
{
TODO ("invoke some hook for instrumentation?");
uiBus_.act (prototype.bind(std::forward<Rec>(arguments)));
}
/**
*
* @param id
* @return
* 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.
*/
string
fun (string& id)
void
Tangible::issueCommand (InvocationTrail const& preparedAction)
{
return "x"+id;
TODO ("invoke some hook for instrumentation?");
uiBus_.act (prototype.bang());
}
}} // namespace gui::model

View file

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

View file

@ -178,6 +178,8 @@ namespace control {
////////////////////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&
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,

View file

@ -133,6 +133,9 @@ namespace control {
template<typename TYPES>
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 undo () ;
@ -171,6 +174,8 @@ namespace control {
void duplicate_detected (Symbol) const;
Symbol getID() const;
operator string() 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>
</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
As a starting point, we know
* 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.
&amp;rarr; InteractionControl
&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>
</div>
<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="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="1448683561882" ID="ID_839961131" MODIFIED="1448683880173">
<node CREATED="1448683561882" ID="ID_839961131" MODIFIED="1448691299107">
<richcontent TYPE="NODE"><html>
<head>
@ -260,6 +260,7 @@
</html>
</richcontent>
<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"/>
</node>
</node>
@ -279,7 +280,7 @@
<node CREATED="1448683674443" ID="ID_1931753413" MODIFIED="1448683684206" TEXT="Einheitlichkeit gef&#xe4;hrdet"/>
</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"/>
<node CREATED="1448683636672" ID="ID_1485949406" MODIFIED="1448683647815" TEXT="verhindert Wildwuchs"/>
<node CREATED="1448683475853" ID="ID_24599710" MODIFIED="1448683492586">
@ -304,6 +305,90 @@
<node CREATED="1448683525822" ID="ID_1395068730" MODIFIED="1448683529258" TEXT="Gesten"/>
</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>