Timeline: publish virtual CSS path and style context via Advice system

...and perform the initialisation once, when attaching the first timeline to the UI
Now our code produces the following Gtk::WidgetPath (note the last node, which our code added)

window:backdrop:dir-ltr.background box:backdrop:dir-ltr.vertical box:backdrop:dir-ltr[2/3].horizontal widget:backdrop:dir-ltr[2/2] widget:backdrop:dir-ltr paned:backdrop:dir-ltr.vertical widget:backdrop:dir-ltr box:backdrop:dir-ltr.vertical notebook:backdrop:dir-ltr[1/1].frame paned:backdrop:dir-ltr.horizontal box:backdrop:dir-ltr.vertical fork.timeline
This commit is contained in:
Fischlurch 2019-07-13 18:04:02 +02:00
parent 6b4bf0a6ea
commit 826df93955
12 changed files with 164 additions and 40 deletions

View file

@ -57,6 +57,14 @@ namespace stage {
const Symbol MARK_Warning{"Warning"};
const Symbol MARK_Message{"Message"};
/* ======== CSS selector names ======== */
const Symbol NODE_fork{"fork"};
const Symbol CLASS_timeline{"timeline"};
/** */

View file

@ -97,5 +97,12 @@ namespace stage {
extern const Symbol MARK_Message;
/* ======== CSS selector names ======== */
extern const Symbol NODE_fork;
extern const Symbol CLASS_timeline;
}// namespace stage
#endif /*STAGE_INTERFACE_UI_PROTOCOL_H*/

View file

@ -190,4 +190,12 @@ namespace ctrl {
}
void
UiManager::configureStyle (timeline::TimelineWidget const& timeline)
{
uiStyle_->prepareStyleContext (timeline);
}
}}// namespace stage::ctrl

View file

@ -59,7 +59,8 @@ namespace stage {
class UiBus;
namespace workspace { class UIStyle; }
namespace workspace { class UiStyle; }
namespace timeline { class TimelineWidget; }
namespace ctrl {
using std::unique_ptr;
@ -146,6 +147,8 @@ namespace ctrl {
void allowCloseWindow (bool yes);
void configureStyle (timeline::TimelineWidget const&);
private:
};

View file

@ -364,6 +364,7 @@ namespace interact {
.addTimeline (
anchorProxy.buildTimelineWidget (this->uiBus_));
globalCtx_.uiManager_.configureStyle (*anchorProxy);
return anchorProxy;
}

View file

@ -57,30 +57,6 @@
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();
// build a "virtual" CSS node to represent the track scope
GType scopeNode = Gtk::Box::get_type();
int pos = path.path_append_type (scopeNode);
// override the generic node name with a custom widget type
gtk_widget_path_iter_set_object_name (path.gobj(), pos, "TrackScope");
// deliberately we don't invoke path.iter_set_name(pos, "id") add an #ID
path.iter_add_class(pos, "timeline");
// for reference, see gtk_widget_path_append_for_widget() in in gtkwidget.c, line 16413
// create a new style context and configure it according to the defined path
PStyleContext style = Gtk::StyleContext::create();
style->set_path (path);
// publish as Advice "style(trackBody)"
styleAdvice.setAdvice (style);
INFO (stage, "Body-CSS: path=%s", util::cStr (path.to_string())); ////////////////////////TICKET #1201 : this yields "paned:dir-ltr.horizontal box:dir-ltr.vertical TrackScope.timeline"
}
}
@ -94,8 +70,6 @@ namespace timeline {
{
topLevelContainer.add1 (headerPane_);
topLevelContainer.add2 (bodyCanvas_);
provideStyleContextAdvice (bodyCanvas_);
}
@ -118,6 +92,13 @@ namespace timeline {
signalStructureChange_(); // this _is_ such a change
}
Gtk::WidgetPath
TimelineLayout::getBodyWidgetPath() const
{
return bodyCanvas_.get_path();
}
/* ==== Interface: LayoutManager===== */

View file

@ -124,6 +124,9 @@ namespace timeline {
/** @internal anchor the display of the root track into the two display panes */
void installRootTrack (TrackHeadWidget&,TrackBody&);
Gtk::WidgetPath getBodyWidgetPath() const;
protected:/* ==== Interface: LayoutManager===== */
PixSpan getPixSpan() override;

View file

@ -90,6 +90,12 @@ namespace timeline {
{
return control_->getName();
}
Gtk::WidgetPath
TimelineWidget::getBodyWidgetPath() const
{
return layout_->getBodyWidgetPath();
}

View file

@ -145,6 +145,8 @@ namespace timeline {
cuString getLabel() const override;
Gtk::WidgetPath getBodyWidgetPath() const;
public: /* ===== Signals ===== */

View file

@ -32,10 +32,13 @@
#include "stage/workspace/ui-style.hpp"
#include "stage/timeline/timeline-widget.hpp"
#include "stage/config-keys.hpp"
#include "lib/searchpath.hpp"
#include "lib/util.hpp"
#include "include/ui-protocol.hpp"
#include <gtkmm/stylecontext.h>
#include <boost/filesystem.hpp>
@ -68,6 +71,7 @@ namespace workspace {
: Gtk::UIManager()
, iconSearchPath_{Config::get (KEY_ICON_PATH)}
, resourceSerachPath_{Config::get (KEY_UIRES_PATH)}
, styleAdvice_{"style(trackBody)"}
{
Glib::set_application_name (Config::get (KEY_TITLE));
@ -101,6 +105,46 @@ namespace workspace {
/**
* @remarks some implementation notes....
* - GTK defines for each widget a _widget path_, to describe the path
* of widget parent relationships down from the top-level window
* - this Gtk::WidgetPath is then used to match against CSS selectors of the GTK stylesheet,
* either the global system theme, or (with precedence) an application stylesheet (`lumiera.css`)
* - but the Gtk::StyleContext and (and should) also be used outside of the core GTK framework to
* carry out custom drawing tasks, which allows custom drawing to "blend in" with the desktop theme.
* - to support this usage, we construct a "virtual widget" and place it at a systematically correct
* position in the widget hierarchy, causing the corresponding style context to pick up the appropriate
* standard CSS definitions in effect at that point in the hierarchy. Moreover, users may (and shall)
* add custom styling rules in a way to match and affect this "virtual" CSS node.
* - the resulting Gtk::StyleContext is constructed only once, at initialisation. It is used from drawing
* code "deep down" within the TimelineWidget's implementation. The [Advice System](\ref advice.hpp)
* allows to pass such information without the need of a direct relation between provider and consumer.
* - for technical reference how to manage and populate GTK WidgetPath nodes, see the implementation
* of the C-function `gtk_widget_path_append_for_widget()` in in GTK 3.x, gtkwidget.c, line 16413
*/
void
UiStyle::prepareStyleContext (timeline::TimelineWidget const& timeline)
{
// the first Timeline triggers initialisation
if (styleAdvice_.isGiven()) return;
Gtk::WidgetPath path = timeline.getBodyWidgetPath();
GType scopeNode = Gtk::Box::get_type();
int pos = path.path_append_type (scopeNode); // build a "virtual" CSS node to represent the track scope
gtk_widget_path_iter_set_object_name (path.gobj(), pos, NODE_fork); // override the generic node name with a custom widget type "fork"
path.iter_add_class(pos, cuString{CLASS_timeline}); // decorate this CSS node with a CSS class ".timeline" (distinguish it from asset bins)
// deliberately we *do not* invoke path.iter_set_name(pos, "id") to add an #ID
PStyleContext style = Gtk::StyleContext::create(); // create a new style context and configure it according to the path defined thus far
style->set_path (path);
styleAdvice_.setAdvice (style); // publish as Advice "style(trackBody)"
INFO (stage, "Body-CSS: path=%s", util::cStr (path.to_string())); ////////////////////////TICKET #1201 : this yields "paned:dir-ltr.horizontal box:dir-ltr.vertical TrackScope.timeline"
}
Cairo::RefPtr<Cairo::SolidPattern>
UiStyle::readStyleColourProperty (Gtk::Widget& widget
,const gchar * property_name

View file

@ -37,6 +37,7 @@
#define STAGE_WORKSPACE_UI_STYLE_H
#include "stage/gtk-base.hpp"
#include "common/advice.hpp"
#include "lib/nocopy.hpp"
#include <cairomm/cairomm.h>
@ -47,6 +48,7 @@ namespace stage {
namespace model { class Project; } ////////////////////////////////////////////////////TICKET #1048 : rectify UI lifecycle
namespace controller { class Controller; } ////////////////////////////////////////////////////TICKET #1048 : rectify UI lifecycle
namespace timeline { class TimelineWidget; }
class UiBus;
@ -54,6 +56,9 @@ namespace workspace {
using std::string;
using StyleAdvice = lumiera::advice::Provision<PStyleContext>;
/**
@ -70,6 +75,8 @@ namespace workspace {
string iconSearchPath_;
string resourceSerachPath_;
StyleAdvice styleAdvice_;
public:
/** The registered icon size for giant 48x48 px icons.
@ -102,6 +109,23 @@ namespace workspace {
*/
void setTheme (string const& stylesheetName);
/**
* Use the existing TimelineWidget's GTK-WidgetPath to establish a systematic
* CSS styling context, which can be used for theming and styling of Lumiera's
* custom UI elements within the timeline display. Especially, this allows us
* to anchor those custom elements at a dedicated point in the hierarchy, the
* way it is visible for CSS selectors. Thus, either standard CSS rules and
* inheritance take effect on our custom elements, or the GTK stylesheet
* may add dedicated style rules for elements within the timeline display,
* most notably the system of nested scopes in the track fork and the
* display of clips and overlays.
*
* Especially the following selector path can be used
* - `paned.timeline-page > body.timeline > fork.timeline`
*/
void prepareStyleContext (timeline::TimelineWidget const&);
/**
* A utility function which reads a colour style from the GTK Style.
* @param widget The widget to load the style out of.

View file

@ -21534,11 +21534,12 @@
</node>
</node>
</node>
<node CREATED="1562844175946" HGAP="53" ID="ID_770758065" MODIFIED="1562844190795" TEXT="dedizierten StyleContext konstruieren" VSHIFT="4">
<node COLOR="#338800" CREATED="1562844175946" HGAP="53" ID="ID_770758065" MODIFIED="1563033494619" TEXT="dedizierten StyleContext konstruieren" VSHIFT="4">
<icon BUILTIN="button_ok"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1562844888515" ID="ID_1004783054" MODIFIED="1562844917833" TEXT="#1171 investigate virtual widget path">
<icon BUILTIN="forward"/>
</node>
<node CREATED="1562845117699" ID="ID_1145993487" MODIFIED="1562845214506" TEXT="kann man einen WidgetPath k&#xfc;nstlich konstruieren?">
<node COLOR="#435e98" CREATED="1562845117699" FOLDED="true" ID="ID_1145993487" MODIFIED="1563033476487" TEXT="kann man einen WidgetPath k&#xfc;nstlich konstruieren?">
<arrowlink COLOR="#3766bc" DESTINATION="ID_293762905" ENDARROW="Default" ENDINCLINATION="-654;0;" ID="Arrow_ID_711737555" STARTARROW="None" STARTINCLINATION="-2487;0;"/>
<node CREATED="1562845842871" ID="ID_119822604" MODIFIED="1562845853198" TEXT="widget-&gt;get_path()"/>
<node CREATED="1562854489102" ID="ID_1789577981" MODIFIED="1562854522386" TEXT="der erzeugte WidgetPath ist bereits eine Kopie">
@ -21607,7 +21608,7 @@
</node>
</node>
</node>
<node CREATED="1562947149512" ID="ID_1788757764" MODIFIED="1562947168297" TEXT="was/wie kann man an den Pfad anh&#xe4;ngen?">
<node COLOR="#435e98" CREATED="1562947149512" ID="ID_1788757764" MODIFIED="1563033443796" TEXT="was/wie kann man an den Pfad anh&#xe4;ngen?">
<node CREATED="1562947206390" ID="ID_858425792" MODIFIED="1562947209292" TEXT="WidgetPath::path_append_type(GType)"/>
<node CREATED="1562947226168" ID="ID_1423368720" MODIFIED="1562947248239" TEXT="wie kann ich einen GType konstruieren, wenn es das Widget garnicht gibt??">
<node CREATED="1562948746502" ID="ID_1583179714" MODIFIED="1562968161548" TEXT="statische Typ-Funktion des jeweiligen Widget nutzen">
@ -21634,13 +21635,15 @@
</node>
</node>
</node>
<node CREATED="1562972564394" ID="ID_47205096" MODIFIED="1562972572357" TEXT="scheint zu funktionieren...">
<node COLOR="#338800" CREATED="1562972564394" ID="ID_47205096" MODIFIED="1563033473194" TEXT="scheint zu funktionieren...">
<arrowlink DESTINATION="ID_1632267547" ENDARROW="Default" ENDINCLINATION="502;0;" ID="Arrow_ID_211374997" STARTARROW="None" STARTINCLINATION="255;50;"/>
<icon BUILTIN="button_ok"/>
<node CREATED="1562972574838" ID="ID_425700528" MODIFIED="1562972585258" TEXT="bekomme den Pfad: &quot;paned:dir-ltr.horizontal box:dir-ltr.vertical TrackScope.timeline&quot;"/>
<node CREATED="1562972605077" ID="ID_1232397111" MODIFIED="1562972636996" TEXT="das w&#xe4;re das TimelineWidget, dann der BodyCanvas, und die neue virtuelle CSS-Node"/>
</node>
</node>
<node CREATED="1562969880923" ID="ID_1675178851" MODIFIED="1562969892966" TEXT="damit einen Gtk::StyleContext konfigurieren">
<node CREATED="1562969894183" ID="ID_1066073394" MODIFIED="1562969914243" TEXT="StyleContext per Factory erzeugen">
<node COLOR="#338800" CREATED="1562969894183" ID="ID_1066073394" MODIFIED="1563033488691" TEXT="StyleContext per Factory erzeugen">
<richcontent TYPE="NOTE"><html>
<head>
@ -21652,8 +21655,9 @@
</body>
</html>
</richcontent>
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1562970409637" ID="ID_156221819" MODIFIED="1562970782190" STYLE="fork" TEXT="ihm den Pfad zuweisen">
<node COLOR="#338800" CREATED="1562970409637" ID="ID_156221819" MODIFIED="1563033490587" TEXT="ihm den Pfad zuweisen">
<richcontent TYPE="NOTE"><html>
<head>
@ -21674,6 +21678,7 @@
</body>
</html>
</richcontent>
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
@ -21685,7 +21690,7 @@
<node CREATED="1562878948640" ID="ID_1131723159" MODIFIED="1562878968203" TEXT="wer erstellt den zugeh&#xf6;rigen StyleContext, und wann?"/>
<node CREATED="1562878977278" ID="ID_115944776" MODIFIED="1562878981971" TEXT="was genau wird &#xfc;bergeben"/>
</node>
<node CREATED="1562878987389" ID="ID_1874620900" MODIFIED="1562878995857" TEXT="Idee: das Advice-System nutzen">
<node COLOR="#435e98" CREATED="1562878987389" FOLDED="true" ID="ID_1874620900" MODIFIED="1563033512000" TEXT="Idee: das Advice-System nutzen">
<icon BUILTIN="idea"/>
<node CREATED="1562879000849" ID="ID_1256390103" MODIFIED="1562879051816" TEXT="sieht nach einem perfekten Anwendungsfall aus">
<node CREATED="1562879123963" ID="ID_1855893476" MODIFIED="1562879144620" TEXT="&quot;irgendjemand&quot; fragt nach einem symbolisch identifizierten StyleContext"/>
@ -21716,14 +21721,46 @@
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1562972719033" ID="ID_1528071219" MODIFIED="1563020034710" TEXT="kl&#xe4;ren: ist der Style-Pfad sinnvoll / korrekt?">
<linktarget COLOR="#46558a" DESTINATION="ID_1528071219" ENDARROW="Default" ENDINCLINATION="-639;0;" ID="Arrow_ID_1602346272" SOURCE="ID_678877446" STARTARROW="None" STARTINCLINATION="564;0;"/>
<icon BUILTIN="help"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1562972742894" ID="ID_340313615" MODIFIED="1562972762823" TEXT="man sollte die custom-widgets explizit benennen">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1562972764593" ID="ID_315266947" MODIFIED="1562972781566" TEXT="warum beginnt der Pfad mit dem Timeline-Widget?">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#435e98" CREATED="1562972764593" ID="ID_315266947" MODIFIED="1563033166705" TEXT="warum beginnt der Pfad mit dem Timeline-Widget?">
<node CREATED="1562972783861" ID="ID_284818197" MODIFIED="1562972793575" TEXT="ist das nicht korrekt veranktert?"/>
<node CREATED="1562972794679" ID="ID_522042066" MODIFIED="1562972802573" TEXT="liegt das an der lib GDL?"/>
<node CREATED="1562972821550" ID="ID_440374994" MODIFIED="1562972834594" TEXT="oder liegt das am Zeitpunkt des Aufrufs?"/>
<node CREATED="1562972821550" ID="ID_440374994" MODIFIED="1563033154868" TEXT="oder liegt das am Zeitpunkt des Aufrufs?">
<icon BUILTIN="forward"/>
</node>
<node COLOR="#338800" CREATED="1563033192867" ID="ID_243229354" MODIFIED="1563033251713" TEXT="besser erst dann aufrufen, wenn das TimelineWidget schon eingeh&#xe4;ngt ist">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...d.h. dann aus dem InteractionDirector heraus
</p>
<p>
-- <i>ja dann </i>kann man auch gleich direkt an den UiManager delegieren!
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="button_ok"/>
</node>
</node>
<node CREATED="1563033292565" ID="ID_1632267547" MODIFIED="1563033473194" TEXT="aktuell ist der Pfad schon sinnvoll (aber noch nicht ideal)">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
window:backdrop:dir-ltr.background box:backdrop:dir-ltr.vertical box:backdrop:dir-ltr[2/3].horizontal widget:backdrop:dir-ltr[2/2] widget:backdrop:dir-ltr paned:backdrop:dir-ltr.vertical widget:backdrop:dir-ltr box:backdrop:dir-ltr.vertical notebook:backdrop:dir-ltr[1/1].frame paned:backdrop:dir-ltr.horizontal box:backdrop:dir-ltr.vertical fork.timeline
</p>
</body>
</html>
</richcontent>
<linktarget COLOR="#a9b4c1" DESTINATION="ID_1632267547" ENDARROW="Default" ENDINCLINATION="502;0;" ID="Arrow_ID_211374997" SOURCE="ID_47205096" STARTARROW="None" STARTINCLINATION="255;50;"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1562972742894" ID="ID_340313615" MODIFIED="1563033187300" TEXT="man sollte die custom-widgets explizit benennen">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>