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:
parent
6b4bf0a6ea
commit
826df93955
12 changed files with 164 additions and 40 deletions
|
|
@ -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"};
|
||||
|
||||
|
||||
/** */
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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*/
|
||||
|
|
|
|||
|
|
@ -190,4 +190,12 @@ namespace ctrl {
|
|||
}
|
||||
|
||||
|
||||
void
|
||||
UiManager::configureStyle (timeline::TimelineWidget const& timeline)
|
||||
{
|
||||
uiStyle_->prepareStyleContext (timeline);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}}// namespace stage::ctrl
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -364,6 +364,7 @@ namespace interact {
|
|||
.addTimeline (
|
||||
anchorProxy.buildTimelineWidget (this->uiBus_));
|
||||
|
||||
globalCtx_.uiManager_.configureStyle (*anchorProxy);
|
||||
return anchorProxy;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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===== */
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -90,6 +90,12 @@ namespace timeline {
|
|||
{
|
||||
return control_->getName();
|
||||
}
|
||||
|
||||
Gtk::WidgetPath
|
||||
TimelineWidget::getBodyWidgetPath() const
|
||||
{
|
||||
return layout_->getBodyWidgetPath();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -145,6 +145,8 @@ namespace timeline {
|
|||
|
||||
cuString getLabel() const override;
|
||||
|
||||
Gtk::WidgetPath getBodyWidgetPath() const;
|
||||
|
||||
|
||||
public: /* ===== Signals ===== */
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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ünstlich konstruieren?">
|
||||
<node COLOR="#435e98" CREATED="1562845117699" FOLDED="true" ID="ID_1145993487" MODIFIED="1563033476487" TEXT="kann man einen WidgetPath kü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->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ängen?">
|
||||
<node COLOR="#435e98" CREATED="1562947149512" ID="ID_1788757764" MODIFIED="1563033443796" TEXT="was/wie kann man an den Pfad anhä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: "paned:dir-ltr.horizontal box:dir-ltr.vertical TrackScope.timeline""/>
|
||||
<node CREATED="1562972605077" ID="ID_1232397111" MODIFIED="1562972636996" TEXT="das wä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örigen StyleContext, und wann?"/>
|
||||
<node CREATED="1562878977278" ID="ID_115944776" MODIFIED="1562878981971" TEXT="was genau wird ü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=""irgendjemand" 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ä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ä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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue