Timeline: establish a way to pass a StyleContext via Advice system
- at some (yet to be defined) location, a virtual WidgetPath is constructed and used to build a Gtk::StyleContext in accordance to the curren CSS - within the drawing routine, we use Lumiera's Advice-System to access this info
This commit is contained in:
parent
06163f6016
commit
60d28fea2c
8 changed files with 1911 additions and 1231 deletions
|
|
@ -27,18 +27,22 @@
|
|||
** is a system wide singleton service, but clients never talk directly to this singleton; rather they
|
||||
** use advice::Provision and advice::Request as access point.
|
||||
**
|
||||
** \par Advice collaboration pattern
|
||||
** Advice collaboration is a special pattern of interaction extracted from multiple use cases within
|
||||
** Lumiera. Creating this abstraction was partially inspired by aspect oriented programming, especially
|
||||
** the idea of cross-cutting the primary dependency hierarchy. Another source of inspiration where the
|
||||
** various incarnations of properties with dynamic binding. For defining the actual binding, we rely
|
||||
** on predicate notation and matching (planned: unification) as known from rule based systems.
|
||||
** # Advice collaboration pattern
|
||||
**
|
||||
** <b>Definition</b>: Advice is an optional, mediated collaboration between entities taking on
|
||||
** the roles of advisor and advised, thereby passing a custom piece of advice data, managed by
|
||||
** the advice support system. The possibility of advice is created by both of the collaborators
|
||||
** entering the system, where the advised entity exposes a point of advice, while the advising
|
||||
** entity provides an actual advice value.
|
||||
** \par rationale
|
||||
** Advice collaboration is a special pattern of interaction extracted from multiple use cases within
|
||||
** Lumiera. Advice can be used as a *Whiteboard System* for exchange of dynamic facts, without imposing
|
||||
** any direct relationship onto the participants. Initially, this abstraction was to some degree inspired
|
||||
** by aspect oriented programming, especially the idea of cross-cutting the primary dependency hierarchy.
|
||||
** Another source of inspiration where the various incarnations of properties with dynamic binding.
|
||||
** For defining the actual binding, we rely on predicate notation and matching (planned: unification)
|
||||
** as known from rule based systems.
|
||||
**
|
||||
** *Definition*: Advice is an optional, mediated collaboration between entities taking on
|
||||
** the roles of _Advisor_ and _Advised_, thereby passing a custom piece of advice data, managed
|
||||
** by the advice support system. The possibility of advice is created by both of the collaborators
|
||||
** entering the system, in any order, where the advised entity exposes a point of advice, while the
|
||||
** advising entity provides an actual advice value.
|
||||
**
|
||||
** \par Collaborators
|
||||
** - the advised entity
|
||||
|
|
@ -48,35 +52,36 @@
|
|||
** - the binding
|
||||
** - the advice
|
||||
**
|
||||
** Usually, the \em advised entity opens the collaboration by requesting an advice. The \em advice itself
|
||||
** is a piece of data of a custom type, which needs to be copyable. Obviously, both the advised and the
|
||||
** advisor need to share knowledge about the meaning of this advice data. The actual advice collaboration
|
||||
** happens at a \em point-of-advice, which needs to be derived first. To this end, the advised puts up an
|
||||
** \em request by providing his \em binding, which is a pattern for matching. An entity about to give advice
|
||||
** opens possible advice \em channels by putting up an advisor binding, which similarly is a pattern. The
|
||||
** advice \em system as mediator resolves both sides, by matching (which in the most general case could be
|
||||
** an unification). This process creates an advice point \em solution -- allowing the advisor to fed the
|
||||
** piece of advice into the advice channel, causing it to be placed into the point of advice. After passing
|
||||
** a certain (implementation defined) break point, the advice leaves the influence of the advisor and gets
|
||||
** Usually, the _advised entity_ opens the collaboration by requesting an advice. The _advice_ itself
|
||||
** is a _piece of data_ of a custom type, which needs to be _copyable_. Obviously, both the advised and
|
||||
** the advisor need to share knowledge about the meaning of this advice data. The actual advice collaboration
|
||||
** happens at a _point-of-advice_, which needs to be derived first. To this end, the advised puts up an
|
||||
** _request_ by providing his _binding_, which is a pattern for matching. An entity about to give advice
|
||||
** opens possible _advice channels_ by putting up an _advisor binding_, which similarly is a pattern. The
|
||||
** _advice system_ as mediator resolves both sides, by matching (which in the most general case could be
|
||||
** an unification). This matching process creates an advice point _solution_ -- the advisor is now able to fed
|
||||
** the piece of advice into the advice channel, causing it to be placed into the point of advice. After passing
|
||||
** a certain (implementation defined) barrier point, the advice leaves the influence of the advisor and gets
|
||||
** exposed to the advised entities. Especially this involves copying the advice data into a location managed
|
||||
** by the advice system. In the standard case, the advised entity accesses the advice synchronously and
|
||||
** non-blocking. Typically, the advice data type is default constructible and thus there is always a basic
|
||||
** form of advice available, thereby completely decoupling the advised entity from the timings related
|
||||
** to this collaboration.
|
||||
**
|
||||
** \par interfaces and implementation
|
||||
** # Interfaces and implementation
|
||||
**
|
||||
** Client code is assumed to interface solely through the advice::Request and advice::Provision classes,
|
||||
** which both can be instantiated and copied freely, may be used as member or mixed in as baseclass.
|
||||
** The AdviceSystem on the other hand is an implementation facility (actually a singleton) and lives
|
||||
** in the advice.cpp translation unit. The interface entities inherit protected from AdviceLink,
|
||||
** which is implemented in the same scope as the AdviceSystem and thus allowed to talk to it
|
||||
** directly. The AdviceSystem in turn uses advice::Index to keep track of the collaboration
|
||||
** in the advice.cpp translation unit. The interface entities mix-in the protected implementation from
|
||||
** AdviceLink, which is implemented in the same scope as the AdviceSystem and thus is allowed to talk
|
||||
** to it directly. The AdviceSystem in turn uses advice::Index to keep track of the collaboration
|
||||
** partners, which, for this purpose, are handled as type-erased PointOfAdvice elements.
|
||||
** The latter class contains 4 API functions used by the index to manage solutions.
|
||||
** The latter class contains 4 API functions used by the index to manage solutions.
|
||||
**
|
||||
** @note as of 6/2010 this is an experimental setup and implemented just enough to work out
|
||||
** the interfaces and gain practical usage experiences. Ichthyo expects this collaboration
|
||||
** service to play a central role at various places within steam-layer.
|
||||
** service to gain relevance over time for several use cases within steam-layer.
|
||||
** @todo allow variables in binding patterns
|
||||
** @todo use the lumiera MPool instead of heap allocations
|
||||
** @todo consider to provide variations of the basic behaviour by policy classes
|
||||
|
|
@ -115,7 +120,7 @@ namespace advice {
|
|||
* as used internally by the AdviceSystem to manage the participants.
|
||||
* Each PointOfAdvice is characterised by a binding pattern, used to
|
||||
* pair up advice::Request and advice::Provision entries. Moreover,
|
||||
* each PointOfAdvice can refer to an existing advice solution
|
||||
* each PointOfAdvice can refer to an existing advice solution
|
||||
* provided elsewhere in the system. The specific type of advice
|
||||
* (and thus the storage requirements) are abstracted away,
|
||||
* as is the distinction between Request and Provision.
|
||||
|
|
@ -219,8 +224,8 @@ namespace advice {
|
|||
/**
|
||||
* Access point for the advising entity (server).
|
||||
* This is the interface intended for client code to set and provide
|
||||
* concrete advice information of a specific type AD. Instantiating
|
||||
* automatically creates a \em type-guard binding pattern, but client code
|
||||
* concrete advice information of a specific type AD. Instantiating
|
||||
* automatically creates a _type-guard_ binding pattern, but client code
|
||||
* can (and typically should) provide additional predicates to define the
|
||||
* "topic" this advice belongs to. This allows advice::Request entries
|
||||
* to attach to the suitable advice "channels" and get the specific
|
||||
|
|
@ -228,9 +233,9 @@ namespace advice {
|
|||
*
|
||||
* Any advice::Provision remains inactive and thus invisible, until
|
||||
* \link #setAdvice setting the concrete advice data.\endlink After that,
|
||||
* the provided data is \em copied into the AdviceSystem and remains available
|
||||
* even after the original Provision goes out of scope. Consequently, it isn't
|
||||
* possible to \em modify advice data once set. But client code may retract
|
||||
* the provided data is _copied_ into the AdviceSystem and remains available
|
||||
* even after the original Provision goes out of scope. Consequently, it is
|
||||
* _not possible to modify_ advice data once set. But client code may _retract_
|
||||
* the provision or change the binding pattern.
|
||||
*
|
||||
* @see AdviceBasics_test usage example
|
||||
|
|
@ -243,7 +248,7 @@ namespace advice {
|
|||
|
||||
/* == policy definitions == */ ////TODO: extract into policy classes
|
||||
|
||||
void deregistrate() { /* NOP */ }
|
||||
void deregister() { /* NOP */ }
|
||||
|
||||
|
||||
public:
|
||||
|
|
@ -254,7 +259,7 @@ namespace advice {
|
|||
|
||||
~Provision()
|
||||
{
|
||||
this->deregistrate();
|
||||
this->deregister();
|
||||
}
|
||||
|
||||
Provision (Provision const& o)
|
||||
|
|
@ -308,7 +313,7 @@ namespace advice {
|
|||
* any requests which happen to match the binding.
|
||||
*
|
||||
* @note the ptr-to-solution in the inherited PointOfAdvice
|
||||
* is currently (5/10) not used, because this \em is
|
||||
* is currently (5/10) not used, because this _is_
|
||||
* already the solution.
|
||||
*/
|
||||
template<class AD>
|
||||
|
|
@ -393,7 +398,7 @@ namespace advice {
|
|||
typedef const ActiveProvision<AD> AdviceProvision;
|
||||
AdviceProvision* solution = static_cast<AdviceProvision*> (getSolution());
|
||||
|
||||
if (solution) // create copy of the data holder, using the new binding
|
||||
if (solution) // create copy of the data holder, using the new binding
|
||||
publishProvision(
|
||||
storeCopy (solution->getAdvice()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
* *****************************************************/
|
||||
|
||||
|
||||
/** @file advice.cpp
|
||||
/** @file advice.cpp
|
||||
** Implementation of the AdviceSystem, to support the advice collaboration.
|
||||
** The AdviceSystem is implemented as singleton, but is never accessed directly
|
||||
** by clients participating in an advice collaboration. Rather, they use the
|
||||
|
|
@ -57,7 +57,7 @@
|
|||
** The problem with copying and incorporating the ActiveProvision objects is the undetermined
|
||||
** size of these value holders, because the frontend objects are templated on the advice type,
|
||||
** while the AdviceSystem doesn't have any knowledge of the specific advice type. This advice
|
||||
** type is used to set a type guard predicate into each binding, but there is no way to
|
||||
** type is used to set a type guard predicate into each binding, but there is no way to
|
||||
** re-discover the specifically typed context; the type guards can only be checked for a match.
|
||||
** Thus we need the help of the frontend objects, which need to provide a deleter function
|
||||
** when providing concrete advice data; this deleter function will be saved as function pointer
|
||||
|
|
@ -113,11 +113,11 @@ namespace advice {
|
|||
|
||||
namespace { // ======= implementation of the AdviceSystem ============
|
||||
|
||||
/**
|
||||
/**
|
||||
* the system-wide service to support the implementation
|
||||
* of \em advice collaborations. Manages storage for
|
||||
* of _advice collaborations_. Manages storage for
|
||||
* provided advice data and maintains an index table
|
||||
* to determine the advice solutions on request.
|
||||
* to determine the advice solutions on request.
|
||||
*/
|
||||
class AdviceSystem
|
||||
: public lib::Sync<>
|
||||
|
|
@ -149,7 +149,7 @@ namespace advice {
|
|||
* by the AdviceSystem monitor. Currently we don't need
|
||||
* locking (heap allocation), but any custom allocator
|
||||
* will have to care for its own locking!
|
||||
*/
|
||||
*/
|
||||
void*
|
||||
allocateBuffer(size_t siz)
|
||||
{
|
||||
|
|
@ -165,8 +165,8 @@ namespace advice {
|
|||
|
||||
void
|
||||
releaseBuffer (void* buff, size_t) /////////////////////////////////TICKET #609
|
||||
{
|
||||
delete[] (char*)buff;
|
||||
{
|
||||
delete[] (char*)buff;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -293,7 +293,7 @@ namespace advice {
|
|||
* which information is available initially, when the advice data is
|
||||
* copied into the system. The knowledge about the size of the allocation
|
||||
* is embodied into the deleter function. This allows later to discard
|
||||
* entries without needing to know their exact type.
|
||||
* entries without needing to know their exact type.
|
||||
*/
|
||||
void
|
||||
AdviceLink::manageAdviceData (PointOfAdvice* entry, DeleterFunc* how_to_delete)
|
||||
|
|
@ -314,7 +314,7 @@ namespace advice {
|
|||
* which the caller then needs to de-allocate.
|
||||
* The caller is assumed to know the actual type
|
||||
* and thus the size of the entry to deallocate.
|
||||
* Returning \c NULL in case no old entry exists.
|
||||
* Returning `NULL` in case no old entry exists.
|
||||
*/
|
||||
void
|
||||
AdviceLink::publishProvision (PointOfAdvice* newProvision)
|
||||
|
|
@ -329,7 +329,7 @@ namespace advice {
|
|||
/** when advice is retracted explicitly,
|
||||
* after removing the provision index entry
|
||||
* we also need to re-process any requests
|
||||
* which happen to match our binding...
|
||||
* which happen to match our binding...
|
||||
* @return pointer to the existing provision entry,
|
||||
* to be deallocated by the caller, which
|
||||
* is assumed to know it's exact type.
|
||||
|
|
|
|||
|
|
@ -87,8 +87,11 @@
|
|||
/** Lumiera GTK UI implementation root. */
|
||||
namespace stage {
|
||||
|
||||
typedef Glib::ustring uString;
|
||||
typedef const uString cuString;
|
||||
using uString = Glib::ustring;
|
||||
using cuString = const uString;
|
||||
|
||||
using PStyleContext = Glib::RefPtr<Gtk::StyleContext>;
|
||||
using PCairoContext = Cairo::RefPtr<Cairo::Context>;
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
//#include "lib/format-string.hpp"
|
||||
//#include "lib/format-cout.hpp"
|
||||
|
||||
#include "common/advice.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
//#include <algorithm>
|
||||
|
|
@ -63,17 +64,22 @@ using std::move;
|
|||
namespace stage {
|
||||
namespace timeline {
|
||||
|
||||
using CairoC = Cairo::RefPtr<Cairo::Context> const&;
|
||||
using CairoC = PCairoContext const&;
|
||||
using StyleC = PStyleContext const&;
|
||||
|
||||
namespace { // details of track background painting
|
||||
|
||||
const int INITIAL_TIMERULER_HEIGHT_px = 30;
|
||||
|
||||
/** request a pre-defined CSS style context for the track body */
|
||||
lumiera::advice::Request<PStyleContext> trackBodyStyle{"style(trackBody)"};
|
||||
|
||||
|
||||
class TrackGroundingRenderer
|
||||
: public ProfileInterpreter
|
||||
{
|
||||
CairoC cox_;
|
||||
StyleC style_;
|
||||
PixSpan visible_;
|
||||
|
||||
|
||||
|
|
@ -137,6 +143,7 @@ namespace timeline {
|
|||
public:
|
||||
TrackGroundingRenderer (CairoC currentDrawContext, DisplayManager& layout)
|
||||
: cox_{currentDrawContext}
|
||||
, style_{trackBodyStyle.getAdvice()}
|
||||
, visible_{layout.getPixSpan()}
|
||||
{ }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
//#include "lib/format-string.hpp"
|
||||
//#include "lib/format-cout.hpp"
|
||||
|
||||
#include "common/advice.hpp"
|
||||
//#include "lib/util.hpp"
|
||||
|
||||
//#include <algorithm>
|
||||
|
|
@ -56,6 +57,16 @@
|
|||
|
||||
namespace stage {
|
||||
namespace timeline {
|
||||
namespace { /////////////////////////////////////////////////////////////////////////////////////TICKET #1168 : doesn't this rather belong into the global UiStyle manager?
|
||||
void
|
||||
provideStyleContextAdvice (Gtk::Widget const& anchorWidget)
|
||||
{
|
||||
lumiera::advice::Provision<PStyleContext> styleAdvice{"style(trackBody)"};
|
||||
|
||||
Gtk::WidgetPath path = anchorWidget.get_path();
|
||||
////////////////////////////////////////////////////////////////////////////TICKET #1201 : add code here to build a virtual path and construct a StyleContext. See gtk_widget_path_append_for_widget() in gtkwidget.c, 16413
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -69,6 +80,8 @@ namespace timeline {
|
|||
{
|
||||
topLevelContainer.add1 (headerPane_);
|
||||
topLevelContainer.add2 (bodyCanvas_);
|
||||
|
||||
provideStyleContextAdvice (bodyCanvas_);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -99,7 +112,7 @@ namespace timeline {
|
|||
{
|
||||
////////////////////////////////////////////////////////TICKET #1019 : need a "ZoomWindow" here to manage the visible area
|
||||
////////////////////////////////////////////////////////TICKET #1039 : "somehow" wire with the TimelineController to find out the covered span
|
||||
return PixSpan {0, 2048}; ////////////////Lalala Lalü
|
||||
return PixSpan {0, 248}; ////////////////Lalala Lalü
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ namespace workspace {
|
|||
REQUIRE (property_name);
|
||||
|
||||
// TODO: Can we get rid of the GdkColor completely here?
|
||||
///////////////////////////////////////////////////////////TICKET #1168 : yes we can. Gdk::Cairo::set_source_rgba(cr, cxStyle->get_background_color(Gtk::STATE_FLAG_SELECTED)); --> gtkmm3.0-3.22.2/gdk/gdkmm/general.cc
|
||||
GdkColor *color;
|
||||
gtk_widget_style_get(widget.gobj(), property_name, &color, NULL);
|
||||
|
||||
|
|
|
|||
|
|
@ -815,23 +815,30 @@ Combining all these requirements and properties provides the foundation for the
|
|||
|
||||
</pre>
|
||||
</div>
|
||||
<div title="AdviceSituations" modifier="Ichthyostega" created="201004052316" modified="201004100124" tags="Concepts spec design">
|
||||
<pre>[[Advice]] is a pattern extracted from several otherwise unrelated constellations
|
||||
<div title="AdviceSituations" modifier="Ichthyostega" created="201004052316" modified="201907121338" tags="Concepts spec design" changecount="1">
|
||||
<pre>[[Advice]] is a pattern extracted from several otherwise unrelated constellations.
|
||||
For the initial analysis in 2010, several use cases were investigated -- ironically, as of 2019, none of these initial use cases became actually relevant. Non the less, the AdviceImplementation turned out to be viable as a ''whiteboard system'' to exchange dynamic facts without coupling.
|
||||
|
||||
!Proxy media in the engine
|
||||
!Actual usages of the Advice System
|
||||
* for access to time grid and timecode format definitions
|
||||
* for access to custom defined UI style elements
|
||||
|
||||
!Historical / Theoretical use-cases
|
||||
|
||||
!!!Proxy media in the engine
|
||||
Without rebuilding the engine network, we need the ability to reconfigure some parts to adapt to low resolution place-holder media temporarily. The collaboration required to make this happen seems to ''cross-cut'' the normal processing logic. Indeed, the nature of the adjustments is highly context dependent &mdash; not every processing node needs to be adjusted. There is a dangerous interference with the ongoing render processes, prompting for the possibility to pick up this information synchronously.
|
||||
* the addressing and delivery of the advice is based on a mix of static (type) and dynamic information
|
||||
* it is concievable that the actual matching may even include a token present in the direct invocation context (but this possibility was ruled out by later decision)
|
||||
* the attempt to recieve and pick up advice needs to be failsafe
|
||||
* locking should be avoided by design
|
||||
|
||||
!Dependency injection for testing
|
||||
!!!Dependency injection for testing
|
||||
While inversion of control is a guiding principle on all levels, the design of the Lumiera application deliberately stays just below the level of employing a dependency injection container. Instead, common services are accessible //by type// and the builder pattern is used more explicitly at places. Interestingly, the impact on writing unit tests was by far not so serious as one might expect, based on the usual reasoning of D.I. proponents. But there remain some situations, where sharing a common test fixture would come in handy
|
||||
* here the test depending on a fixture puts up a hard requirement for the actual advice to be there.
|
||||
* thus, the advice support system can be used to communicate a need for advice
|
||||
* but it seems unreasonable to extend it actually to transmitt a control flow
|
||||
|
||||
!properties of placement
|
||||
!!!properties of placement
|
||||
The placement concept plays a fundamental role within Lumiera's HighLevelModel. Besides just being a way of sticking objects together and defining the common properties of //temporal position and output destination,// we try to push this approach to enable a more general, open and generic use. "Placement" is understood as locating within a grid comprised of various degrees of freedom &mdash; where locating in a specific way might create additional dimensions to be included into the placement. The standard example is an output connection creating additional adjustable parameters controlling the way the connected object is embedded into a given presentation space (consider e.g. a sound object, which &mdash; just by connection, gains the ability of being //panned// by azimuth, elevation and distance)
|
||||
* in this case, obviously the colaboration is n:m, while each partner preferrably should only see a single advice link.
|
||||
* advice is used here to negotiate a direct colaboration, which is then handed off to another facility (wiring a control connection)
|
||||
|
|
@ -3517,7 +3524,7 @@ In accordance with the Lumiera application architecture in general, the UI is no
|
|||
<pre>A specially configured LumieraPlugin, which actually contains or loads the complete code of the (GTK)GUI, and additionally is linked dynamically against the application core lib. During the [[UI startup process|GuiStart]], loading of this Plugin is triggered from {{{main()}}}. Actually this causes spawning of the GTK event thread and execution of the GTK main loop.
|
||||
</pre>
|
||||
</div>
|
||||
<div title="GuiTimelineDraw" creator="Ichthyostega" modifier="Ichthyostega" created="201904052156" modified="201906101247" tags="GuiPattern spec impl img draft" changecount="92">
|
||||
<div title="GuiTimelineDraw" creator="Ichthyostega" modifier="Ichthyostega" created="201904052156" modified="201906291535" tags="GuiPattern spec impl img draft" changecount="93">
|
||||
<pre>The presentation of the track body area relies on the [[Gtk::Layout "canvas widget"|GtkLayoutWidget]], thus allowing for a mixture of custom drawing with embedded custom Gtk widgets. The actual drawing routine is activated in response to the {{{on_draw}}} signal -- and invoking the inherited handler function will initiate the standard drawing for the embedded child widgets. This partitions the additional, specific drawing activities into a pre-widget drawing phase to prepare the background and framework structure of the track area, and a post-widget drawing phase to show all kinds of overlays, markers cursors and similar UI indicators. A nested structure of {{{TrackBody}}} objects serves as organisational device to structure these custom drawing activities in accordance with the nested structure of the track fork.
|
||||
|
||||
!Building a nested 3D structure
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue