diff --git a/src/stage/lumiera-light-theme-complement.css b/src/stage/lumiera-light-theme-complement.css index 1eba6dc61..461bb2d19 100644 --- a/src/stage/lumiera-light-theme-complement.css +++ b/src/stage/lumiera-light-theme-complement.css @@ -88,4 +88,5 @@ */ .fork__bracket { padding-left: 2px; + padding-right: 1px; } diff --git a/src/stage/timeline/stave-bracket-widget.cpp b/src/stage/timeline/stave-bracket-widget.cpp index d7e9cf617..7ebbde256 100644 --- a/src/stage/timeline/stave-bracket-widget.cpp +++ b/src/stage/timeline/stave-bracket-widget.cpp @@ -41,10 +41,9 @@ ** defined standard font in device units, and uses this _em_ size as reference to derive a _scale_ factor, ** which is then applied to the drawing as a whole — taking into account any given vertical size limitations ** as imposed by the general nested trade head structure. - ** - the FreeCAD document can be found at `doc/devel/draw/StaveBracket.FCStd` - ** - see also the SVG image `doc/devel/draw/StaveBracket.svg` for explanation of geometry - ** - ** @todo WIP as of 3/2023 + ** @see the FreeCAD document can be found at `doc/devel/draw/StaveBracket.FCStd` + ** @see SVG image `doc/devel/draw/StaveBracket.svg` for explanation of geometry + ** @see explanation on page #TrackStaveBracket in the TiddlyWiki ** */ @@ -73,7 +72,7 @@ namespace timeline { const double BASE_WIDTH_PER_EM = 0.5; // scale factor: width of double line relative to font size const double ORG = 0.0; - const double PHI = (1.0 + sqrt(5)) / 2.0; // Golden Ratio Φ ≔ ½(1+√5) ≈ 1.6180339887498948482 + const double PHI = (1.0 + sqrt(5)) / 2.0; // Golden Ratio Φ ≔ ½·(1+√5) ≈ 1.6180339887498948482 const double PHI_MAJOR = PHI - 1.0; // 1/Φ = Φ-1 const double PHI_MINOR = 2.0 - PHI; // 1-1/Φ = 2-Φ const double PHISQUARE = 1.0 + PHI; // Φ² = Φ+1 @@ -150,7 +149,9 @@ namespace timeline { double determineScale (StyleC style, int givenHeight) { - auto maxScale = givenHeight / (2*PHISQUARE); + auto required = 2*PHISQUARE + style->get_padding().get_top() + + style->get_padding().get_bottom(); + auto maxScale = givenHeight / (required); return min (maxScale, baseWidth (style)); } @@ -162,14 +163,20 @@ namespace timeline { int calcRequiredWidth (StyleC style, int givenHeight) { - return ceil (PHISQUARE * determineScale (style,givenHeight)); + return ceil (PHISQUARE * determineScale (style,givenHeight) + +style->get_padding().get_right() + +style->get_padding().get_left() + ); } /** @return width for the drawing, without considering height limitation */ int calcDesiredWidth (StyleC style) { - return ceil (PHISQUARE * baseWidth (style)); + return ceil (PHISQUARE * baseWidth (style) + +style->get_padding().get_right() + +style->get_padding().get_left() + ); } /** place left anchor reference line to right side of bold bar. @@ -260,6 +267,70 @@ namespace timeline { cox->restore(); } + /** + * Indicate connection to nested sub-Track scopes. + * Draw a connector dot at each joint, and a arrow + * pointing towards the nested StaveBracket top. + * @todo simplistic implementation as of 3/23; + * could be made expandable /collapsable + */ + void + connect (CairoC cox, Gdk::RGBA colour + ,double leftX, double upperY, double lowerY, double width, double scale + ,std::vector connectors) + { + double limit = lowerY - upperY; + double line = leftX + scale*(LIN_LEFT + LIN_WIDTH/2); + double rad = scale * PHI_MAJOR; + cox->save(); + // shift connectors to join below top cap + cox->translate (line, upperY); + // fill circle with a lightened yellow hue + cox->set_source_rgb(1 - 0.2*(colour.get_red()) + ,1 - 0.2*(colour.get_green()) + ,1 - 0.5*(1 - colour.get_blue()) ); + // draw a circle joint on top of the small vertical line + for (uint off : connectors) + if (off <= limit) + { + cox->move_to(rad,off); + cox->arc ( 0,off, rad, 0, 2 * M_PI); + cox->close_path(); + } + // + cox->fill_preserve(); + cox->set_source_rgba(colour.get_red() + ,colour.get_green() + ,colour.get_blue() + ,colour.get_alpha()); + cox->set_line_width(scale*LIN_WIDTH*PHI_MAJOR); + cox->stroke(); + // + // draw connecting arrows... + cox->translate(rad,0); + // Note: arrow tip uses complete width, reaches into the padding-right + double len = width-line-rad-1; // -1 to create room for a sharp miter + ASSERT (len > 0); + double arr = len * PHI_MINOR; + double bas = scale * PHI_MINOR; + for (uint off : connectors) + if (off <= limit) + { + cox->move_to(ORG,off); + cox->line_to(arr,off); + // draw arrow head... + cox->move_to(arr,off-bas); + cox->line_to(len,off); + cox->line_to(arr,off+bas); + cox->close_path(); + } + cox->set_miter_limit(20); // to create sharp arrow tip + cox->fill_preserve(); + cox->stroke(); + // + cox->restore(); + } + }//(End)Implementation details (drawing design) @@ -271,6 +342,7 @@ namespace timeline { StaveBracketWidget::StaveBracketWidget () : _Base{} + , connectors_{} { get_style_context()->add_class (CLASS_fork_bracket); this->property_expand() = false; @@ -296,6 +368,7 @@ namespace timeline { StyleC style = this->get_style_context(); auto colour = style->get_color (Gtk::STATE_FLAG_NORMAL); int height = this->get_allocated_height(); + int width = this->get_width(); double scale = determineScale (style, height); double left = anchorLeft (style, scale); double upper = anchorUpper (style,scale); @@ -304,6 +377,7 @@ namespace timeline { drawCap (cox, colour, left, upper, scale, true); drawCap (cox, colour, left, lower, scale, false); drawBar (cox, colour, left, upper, lower, scale); + connect (cox, colour, left, upper, lower, width, scale, connectors_); return event_is_handled; } diff --git a/src/stage/timeline/stave-bracket-widget.hpp b/src/stage/timeline/stave-bracket-widget.hpp index 3ffd85f73..4586abd1a 100644 --- a/src/stage/timeline/stave-bracket-widget.hpp +++ b/src/stage/timeline/stave-bracket-widget.hpp @@ -30,13 +30,15 @@ ** the actual space allocation and positioning of sub-tracks in the layout; technicalities ** of actual drawing this structure is abstracted into this custom widget — allowing the ** track head to indicate the necessary layout constraints generic and recursively. + ** The relation to nested stave brackets for sub-Tracks can be indicated with a + ** connection joint and arrow; prerequisite is to provide the vertical offset. ** ** \par styling ** - styling is controlled via CSS, using the marker class \ref CLASS_fork_bracket ** - the »base width« of the vertical double line is based on the font's `em` setting ** - padding and colour attributes from CSS are observed ** - ** @todo WIP as of 3/2023 + ** @see TrackHeadWidget::structure_ ** */ @@ -46,6 +48,8 @@ #include "stage/gtk-base.hpp" +#include + namespace stage { namespace timeline { @@ -64,11 +68,14 @@ namespace timeline { : public Gtk::DrawingArea { using _Base = Gtk::DrawingArea; + std::vector connectors_; public: ~StaveBracketWidget(); + StaveBracketWidget(); - StaveBracketWidget (); + void clearConnectors(); + void addConnector (uint offset); private:/* ===== Internals ===== */ @@ -80,5 +87,26 @@ namespace timeline { }; + + inline void + StaveBracketWidget::clearConnectors() + { + connectors_.clear(); + } + + /** + * Request to draw a connector to the nested sub-Track's stave bracket. + * @param offset vertical location where the sub-Track starts, relative + * to the start of this stave bracket's start + * @remark called from the 2nd DisplayEvaluation pass, when linking the layout + * @see TrackHeadWidget::linkSubTrackPositions + */ + inline void + StaveBracketWidget::addConnector (uint offset) + { + connectors_.emplace_back (offset); + } + + }}// namespace stage::timeline #endif /*STAGE_TIMELINE_STAVE_BRACKET_WIDGET_H*/ diff --git a/src/stage/timeline/track-head-widget.cpp b/src/stage/timeline/track-head-widget.cpp index 88388df4e..0cb941364 100644 --- a/src/stage/timeline/track-head-widget.cpp +++ b/src/stage/timeline/track-head-widget.cpp @@ -81,13 +81,15 @@ namespace timeline { this->attach (headCtrl_, 1,1, 1,1); // corresponds to direct content this->attach (padding_, 1,2, 1,1);// used to sync with sub-track display this->property_expand() = false; // do not expand to fill + this->set_column_spacing(0); + this->set_row_spacing(0); this->show_all(); } HeadControlArea::HeadControlArea() : Gtk::Grid{} - , ctrlTODO_{"💡"} + , ctrlTODO_{"\n 💡"} { get_style_context()->add_class (CLASS_fork_control); ctrlTODO_.set_xalign (0.3); @@ -144,9 +146,14 @@ namespace timeline { void TrackHeadWidget::accommodateOverallHeight(uint overallHeight) { + uint discrepancy{0}; uint localHeight = getOverallHeight(); if (overallHeight > localHeight) - enforceExpansionHeight (overallHeight - getLabelHeight()); + { + enforceExpansionHeight (overallHeight - getLabelHeight()); + discrepancy = overallHeight-localHeight; + } + linkSubTrackPositions (discrepancy); } /** @@ -163,8 +170,29 @@ namespace timeline { if (directHeight > localHeight) enforceSyncPadHeight (directHeight - localHeight); } - - + + /** + * Coordinate the exact positions of sub-Track start during DisplayEvaluaton. + * @note assuming that layout for all sub-Tracks is already final when called + * @param discrepancy additional vertical offset incurred to reach a nominal height; + * this value is interspersed between the content cells and above the Children + */ + void + TrackHeadWidget::linkSubTrackPositions(uint discrepancy) + { + structure_.clearConnectors(); + uint offset = getContentHeight() + + getSyncPadHeight() + + discrepancy + + getLabelHeight() // offset by the label in the children + ; + for (uint child=0; child < childCnt_; ++child) + { + structure_.addConnector (offset); + offset += getHeightAt (1, child+3); + } + } + /** * @remark The Lumiera Timeline model does not rely on a list of tracks, as most conventional diff --git a/src/stage/timeline/track-head-widget.hpp b/src/stage/timeline/track-head-widget.hpp index 5ccd5e291..d0b942d0b 100644 --- a/src/stage/timeline/track-head-widget.hpp +++ b/src/stage/timeline/track-head-widget.hpp @@ -116,6 +116,8 @@ namespace timeline { /** Discard all nested sub track display widgets. */ void clearFork(); + void linkSubTrackPositions (uint); + /** get the height allocated at cell(x,y) */ uint getHeightAt (int left, int top) const; void enforceHeightAt(int left, int top, uint height); diff --git a/wiki/draw/TrackHeadNesting.png b/wiki/draw/TrackHeadNesting.png index 024a8f9b8..8412d5032 100644 Binary files a/wiki/draw/TrackHeadNesting.png and b/wiki/draw/TrackHeadNesting.png differ diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 3bdf70847..5c3d8efa8 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -27227,7 +27227,7 @@ - + @@ -27872,7 +27872,7 @@ - + @@ -27918,9 +27918,22 @@ - + + + + + + + + +

+ z.B. Folding +

+ +
+
@@ -27989,19 +28002,19 @@
- + - + - - + + @@ -28040,12 +28053,12 @@ - - - - - - + + + + + + @@ -28786,8 +28799,8 @@ - - + + @@ -28868,8 +28881,238 @@ - - + + + + + + + + + + + + + + + + + + + + + +

+ ...denn wir müssen sowiso einen globalen Pass machen, und zwar erst spät, wenn das Layout bereits komplett geregelt ist +

+ +
+ + +
+
+ + + + + + + + +

+ TrackHeadWidget::syncSubtrackStartHeight (uint directHeight) +

+ +
+ + + + + + + + +

+ ⤷ kommend von DisplayFrame::sync_and_balance (DisplayEvaluation&) +

+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + +

+ TrackHeadWidget::accommodateOverallHeight(uint overallHeight) +

+ +
+
+ + + + + + + + + + + + + + + + + + + + +
    +
  • + zum Einen habe ich unbemerkt die getLabelHeight() vom Parent zum Child verschleppt. Das ist aber unter dem Strich korrekt, weil jedes Kind wieder mit einem Label beginnt +
  • +
  • + außerdem gibt es da anscheinend im Grid ein zusätzliches Padding, was mir bisher entgangen war +
  • +
+ +
+ +
+ + + + + + + + + + + + +

+ der Expander in der ersten Spalte dient dazu, das TrackHeadWidget auf eine geforderte Nenn-Höhe aufzuspannen. Anscheinend habe ich die Zellen so definiert, daß diese Diskrepanz als spacing zwischen Label, Content und SyncPadding verteilt wird +

+ +
+ +
+ + + +
+
+
+ + + + + + + + + + + + + + + + + + + +

+ mache hier die vereinfachtende Annahme, daß alle Brackets die gleiche Metrik haben +

+ +
+
+ + + + + + +

+ geprüft mit Screenshot in Gimp. +

+

+ Unter der Einschränkung, daß hier viel Aliasing passiert und das Bild schon relativ klein ist +

+ +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ ...der default ist 10, und das erzeugt eine Gehrung nur bis ca 20° +

+ +
+
+ + + +
+
+
@@ -28877,9 +29120,9 @@
- - - + + + @@ -28891,7 +29134,7 @@ - + @@ -29036,7 +29279,7 @@ - + @@ -29314,7 +29557,10 @@ - + + + + @@ -29333,10 +29579,14 @@ - - - - + + + + + + + + @@ -29347,9 +29597,9 @@ + + - - @@ -33977,8 +34227,34 @@ - + + + + + + + + + +

+ ...darüber bin ich auch beim Zeichnen der Connector im StaveBracket gestolpert +

+ +
+
+ + + + + + +

+ sichtbar an der Höhe der Sub-Scope-Verbindungen auf den StaveBrackets +

+ +
+