Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
david_stocker
Contributor
0 Kudos

This is part of a tutorial series on creating extension components for Design Studio.

Last time, we added  guide lines to our raw html sandbox gauge.  Now we're going to bring it into our component.  We have a few things that we'll need to take care of:

  • Since we won't always want to display the radial guide lines and guide arc, so we'll need to make them optional.
  • In part 9, we'll introduce an indicator needle.  When we do that, the colored arc won't be our only display option, so while we're at it, we'll add the main arc to the optional list.
  • The guide ring has the same clockwise orientation as the main gauge arc and if the end angle is smaller than the start angle, it will need to be increased in 360 degree increments until we get a clockwise arc.  You may recall from Part 6a how we solved this.  We won't simply re-use the recalculateCurrentAngle() that we created there, as most of ist code is about measure handling.  Instead, we introduce a new function, recalculateGuideRingAngles, that simply does the while (end <  start) {//Increment;} part. 
  • The guide lines and arc need a color setting, as we may not want them to be the same color as the main gauge arc.
  • The guide ring (arc) will need its own start and end angles. 
  • The guide ring and lines will need their own thickness setting. 
  • For the sake of simplicity, we'll presume that these new settings are the sort of thing that designers want to have precise control over at design time and don't need to be dynamic.  Therefore, the new properties won't be added to the Design Studio script language and we won't need to change contribution.ztl.
  • We'll add a new "Guide Lines" group in the properties pane, where we'll cluster the new properties.

Adding the Properties

We'll add the new group to contribution.xml


  <group


  id="SCNGaugeLineSettings"


  title="Guide Lines"


  tooltip="Guide Line Ring and Line Settings"/>


We'll add the 8 properties, that we defined above as requirements:


<property id="enableGuideLines" title="Enable Guide Lines" type="boolean" group="SCNGaugeLineSettings"/>


<property id="guideColorCode" title="Guide Line Color" type="Color" group="SCNGaugeLineSettings"/>


<property id="bracketThickness" title="Guide Line Thickness" type="int" group="SCNGaugeLineSettings"/>


<property id="enableGuideRing" title="Enable Guide Ring" type="boolean" group="SCNGaugeLineSettings"/>


<property id="ringColorCode" title="Guide Ring Color" type="Color" group="SCNGaugeLineSettings"/>


<property id="ringThickness" title="Guide Ring Thickness" type="int" group="SCNGaugeLineSettings"/>


<property id="ringStartAngleDeg" title="Guide Ring Start Angle" type="float" group="SCNGaugeLineSettings"/>


<property id="ringEndAngleDeg" title="Guide Ring End Angle" type="float" group="SCNGaugeLineSettings"/>


If this component were for production use, we'd be very defensive, checking for the presence of intiialized property values whenever we need them, etc.  For the sake of brevity, we'll give the new properties default values.  This is less reliable than actively checking for undefined property values, but also a lot less verbose.  We'll default to guide ring/lines being blue, not being displayed, being 2 pixels wide and being a full circle (from 0 to 360 degrees).


<initialization>


        ...       


        <defaultValue property="enableGuideLines">false</defaultValue>


        <defaultValue property="guideColorCode">blue</defaultValue>


        <defaultValue property="bracketThickness">2</defaultValue>


        <defaultValue property="enableGuideRing">false</defaultValue>


        <defaultValue property="ringColorCode">blue</defaultValue>


        <defaultValue property="ringThickness">2</defaultValue>


        <defaultValue property="ringStartAngleDeg">0.0</defaultValue>


        <defaultValue property="ringEndAngleDeg">360.0</defaultValue>


</initialization>


Adding the Guides to the Canvas (updating component.js)

Now comes the time for the rubber to meet the road.  We've brought properties into canvas components many timas already, but just to review at a high level; we'll need:

The local Proxies


me._enableArc = true;




//Part 8 Guide Lines


me._enableGuideLines = false;


me._enableGuideRing = false;


me._ringColorCode = 'blue';


me._ringThickness = 2;


me._bracketThickness = 2;


me._ringStartAngleDeg = 0.0;


me._ringEndAngleDeg = 360.0;


The new getter/setter functions:


me.enableArc = function(value) {


  if (value === undefined) {


  return me._enableArc;


  } else {


  me._enableArc = value;


  me.redraw();


  return me;


  }


};




// Part 8


me.enableGuideLines = function(value) {


  if (value === undefined) {


  return me._enableGuideLines;


  } else {


  me._enableGuideLines = value;


  me.redraw();


  return this;


  }


};




me.bracketThickness = function(value) {


  if (value === undefined) {


  return me._bracketThickness;


  } else {


  me._bracketThickness = value;


  me.redraw();


  return this;


  }


};




me.guideColorCode = function(value) {


  if (value === undefined) {


  return me._guideColorCode;


  } else {


  me._guideColorCode = value;


  me.redraw();


  return this;


  }


};




me.enableGuideRing = function(value) {


  if (value === undefined) {


  return me._enableGuideRing;


  } else {


  me._enableGuideRing = value;


  me.redraw();


  return this;


  }


};




me.ringColorCode = function(value) {


  if (value === undefined) {


  return me._ringColorCode;


  } else {


  me._ringColorCode = value;


  me.redraw();


  return this;


  }


};




me.ringThickness = function(value) {


  if (value === undefined) {


  return me._ringThickness;


  } else {


  me._ringThickness = value;


  me.redraw();


  return this;


  }


};




me.ringStartAngleDeg = function(value) {


  if (value === undefined) {


  return me._ringStartAngleDeg;


  } else {


  me._ringStartAngleDeg = value;


  me.redraw();


  return this;


  }


};




me.ringEndAngleDeg = function(value) {


  if (value === undefined) {


  return me._ringEndAngleDeg;


  } else {


  me._ringEndAngleDeg = value;


  me.redraw();


  return this;


  }


};


// End Part 8 Properties


Specific to the new feature, we'll need:

  • recalculateGuideRingAngles() moves into component.js and gets renamed me.recalculateGuideRingAngles()
  • The drawing code moves into the me.redraw() function and we'll refactor the variables to work with the existing code in component.js.  Variables from the html sandbox file that correspond to properties will get a "me._" prefix, width becomes me.$().width(), etc.
  • The newly migrated and refactored code blocks for drawing the guides get wrapped inside if statements and only executed when the designer has decided to enable them. 

The refactored recalculateGuideRingAngles () function:


//New with Part 8


me.recalculateGuideRingAngles = function(){


  //The ring has no max angle or measures, so it is trivial to recalculate.


  //Right now, this gauge is hardcoded to turn in a clockwise manner.


  //  Ensure that the arc can turn in a clockwise direction to get to the end angles


  while (me._ringEndAngleDeg < me._ringStartAngleDeg){


  me._ringEndAngleDeg = me._ringEndAngleDeg + 360.0;


  }


};


The me.redraw() function, after the newly refactored guide drawing code goes in:


//Part 8 - The guide lines


///////////////////////////////////////////


//Lets build a border ring around the gauge


///////////////////////////////////////////


if (me._enableGuideLines == true){


  var visRing = d3.select(myDiv).append("svg:svg").attr("width", "100%").attr("height", "100%");



  var ringOuterRad = me._outerRad + ( -1 * me._ringThickness);  //Outer ring starts at the outer radius of the inner arc




  var ringArcDefinition = d3.svg.arc()


  .innerRadius(me._outerRad)


  .outerRadius(ringOuterRad)


  .startAngle(me._ringStartAngleDeg * (pi/180)) //converting from degs to radians


  .endAngle(me._ringEndAngleDeg * (pi/180)) //converting from degs to radians




  var ringArc = vis


  .append("path")


  .attr("d", ringArcDefinition)


  .attr("fill", me._ringColorCode)


  .attr("transform", "translate(" + me._offsetLeft + "," + me._offsetDown + ")");


}


///////////////////////////////////////////


//Lets build a the start and end lines


///////////////////////////////////////////


if (me._enableGuideRing == true){


  var visStartBracket = d3.select(myDiv).append("svg:svg").attr("width", "100%").attr("height", "100%");


  var lineData = [endPoints (me._outerRad, me._ringStartAngleDeg), {x:me._offsetLeft, y:me._offsetDown}, endPoints (me._outerRad, me._ringEndAngleDeg)];


  var lineFunction = d3.svg.line()


  .x(function(d) { return d.x; })


  .y(function(d) { return d.y; })


  .interpolate("linear");



  var borderLines = vis


  .attr("width", me.$().width()).attr("height", me.$().height()) // Added height and width so line is visible


  .append("path")


  .attr("d", lineFunction(lineData))


  .attr("stroke", me._ringColorCode)


  .attr("stroke-width", me._bracketThickness)


  .attr("fill", "none");


}


The Guide Lines and Arc in Action

PropertiesResult

Main Arc: Enabled

Radian Guide Lines: Enabled

Guide Arc: Enabled

Main Arc: Disabled

Radian Guide Lines: Enabled

Guide Arc: Enabled

Main Arc: Enabled

Radial Guide Lines: Disabled

Guide Arc: Enabled

Main Arc: Enabled

Radian Guide Lines: Enabled

Guide Arc: Disabled

As always, the extension project and test application are in a repository on Github.  Next, time, we'll start adding an indicator needle.