<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> | |
<html> | |
<head> | |
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"> | |
<title>Using styles</title> | |
<link href="../book.css" rel="Stylesheet" type="text/css"> | |
<link href="../code.css" rel="Stylesheet" type="text/css"> | |
</head> | |
<body> | |
<h1>Using Styles</h1> | |
<p>Sometimes graphical tools give the user the possibility to freely change the | |
graphical attributes to paint each shape (color, line-width, …). But more often | |
all shapes of the same kind shall be painted in the same way.</p> | |
<p>For example in a Ecore editor it makes sense, that all EClasses look identical. | |
The user might still have the possibility to change the color of a EClass, but then | |
the color of all other EClasses should be changed, too.</p> | |
<p>The graphics framework supports this by providing "styles". A style is a container | |
for graphical attributes (color, line-width, …), which can be associated by a graphics | |
algorithm. A style can have a parent-style, from which a style can "inherit" graphical | |
attributes (similar to cascading style sheets).</p> | |
<p>Concretely the value of a graphical attribute is determined by finding the first | |
value which is not null in the following order:</p> | |
<ol> | |
<li>The graphics algorithm itself</li> | |
<li>The style assigned to the graphics algorithm, if style exists</li> | |
<li>The parent-style(s) of the style, if parent-style(s) exists</li> | |
</ol> | |
<p>It has several advantages if the same style is associated by many graphics algorithms:</p> | |
<ul> | |
<li>When a graphical attribute value of the style is changed, all graphics algorithms | |
are repainted using this new style</li> | |
<li>A tool could provide a predefined set of styles from which the user can | |
choose (similar to the "themes" available in many programs)</li> | |
<li>Redundant storage of identical information is avoided</li> | |
</ul> | |
<h2>Creating a Style Utility Class</h2> | |
<p>In this example we want to associate the same style to the graphics algorithms | |
of all EClasses and EReferences. <strong>All graphical attributes </strong>(color, line-width, …) | |
<strong>are then set on | |
the style</strong> and no longer on the different graphics algorithm.</p> | |
<p>Additionally we will provide functionality to change the foreground-color-value of that | |
style, and as a result all EClasses will be painted in this color.</p> | |
<p>We start by implementing a utility class for the handling of styles.</p> | |
<p>We store the styles in the diagram with a given ID. The utility class has methods | |
to return a specific style by either finding it in the diagram or by creating a | |
new<strong> </strong>style and assigning it to the diagram. When creating | |
styles, the method <span class="inlinecode">createPlainStyle</span> is used to | |
set explicitly all graphical values here.</p> | |
<p>Note, that we have four styles: one main style with common values for all | |
other child styles. There are three child styles: a child style for graphics algorithms of | |
the EClass and EReference, a child style for the Text of the EClass, and a child | |
style for the Text decorator of the EReference. The reason is, that | |
we want to have a different color for the text and for the lines, but in both cases | |
the color is stored as "foreground" color. The Text of the EClass has | |
a bold Arial font, while the Text decorator has an italic Arial font.</p> | |
<p>We have a cascading design of styles here . C<span id="result_box">omplemental</span><span id="result_box" lang="en"> | |
<span class="hps">we</span> <span class="hps">could</span> <span class="hps"> | |
introduce</span> <span class="hps">another</span> <span class="hps">style for | |
the</span> </span><span id="result_box">common</span><span id="result_box" lang="en"> | |
<span class="hps">values of text</span><span>.</span> <span class="hps">The | |
opposite of this</span></span><span class="hps"><span id="result_box"> cascading | |
approach is</span></span><span><span id="result_box"> to</span><span id="result_box" lang="en"> | |
</span></span><span id="result_box" lang="en"><span class="hps">put</span> | |
<span class="hps">all the styles</span> <span class="hps">flat</span> | |
<span class="hps">side by side.</span></span><span class="hps"><span id="result_box"> | |
The present arrengment was choosen because of the intrinsic clarity and the | |
economical serialization into the diagram file. Do not hesitate to have a look | |
on this by opening the diagram file with a text editor and then search for | |
"styles".</span></span></p> | |
<p>You can see the complete implementation of the style utility class here:</p> | |
<!-- Begin code ------------------------------------------------------------------------------- --> | |
<p> </p> | |
<div class="literallayout"> | |
<div class="incode"> | |
<p class="code"><span class="keyword">package</span> org.eclipse.graphiti.examples.tutorial;<br> <br> | |
<span class="keyword">public class</span> StyleUtil {<br> <br> | |
<span class="keyword">private static final</span> IColorConstant | |
<span class="string"><em>E_CLASS_TEXT_FOREGROUND</em></span> =<br> | |
<span class="keyword"> new</span> ColorConstant(0, 0, 0);<br> | |
<span class="keyword"> private static final</span> IColorConstant<span class="string"><em> | |
E_CLASS_FOREGROUND</em></span> =<br> | |
<span class="keyword">new</span> ColorConstant(98, 131, 167);<br> | |
private static final IColorConstant <span class="string"><strong><em>E_CLASS_BACKGROUND</em></strong></span> | |
=<br> <span class="keyword">new</span> | |
ColorConstant(187, 218, 247);<br><br> | |
<span class="keyword">public static Style</span> getStyleForCommonValues(Diagram | |
diagram) {<br> | |
<span class="keyword">final</span> String styleId = <span class="string"> | |
"COMMON-VALUES"</span>;<br> IGaService | |
gaService = Graphiti.getGaService();<br><br> | |
<span class="comment">// Is style already persisted?</span><br> | |
Style style = gaService.findStyle(diagram, styleId);<br><br> | |
<span class="keyword">if</span> (style == <span class="keyword">null</span>) | |
{ <span class="comment">// style not found - create new style</span><br> | |
style = gaService.createPlainStyle(diagram, styleId);<br> | |
setCommonValues(style);<br> }<br> | |
<span class="keyword">return</span> style;<br> }<br> <br> | |
<span class="keyword"> public static</span> Style getStyleForEClass(Diagram | |
diagram) {<br> | |
<span class="keyword">final</span> String styleId = <span class="string"> | |
"E-CLASS"</span>;<br> IGaService | |
gaService = Graphiti.getGaService();<br><br> | |
<span class="comment">// this is a child style of the common-values-style</span><br> | |
Style parentStyle = <em>getStyleForCommonValues</em>(diagram);<br> | |
Style style = gaService.findStyle(parentStyle, styleId);<br><br> | |
<span class="keyword">if</span> (style == <span class="keyword">null</span>) | |
{ <span class="comment">// style not found - create new style</span><br> | |
style = gaService.createPlainStyle(parentStyle, styleId);<br> | |
style.setFilled(<span class="keyword">true</span>);<br> | |
style.setForeground(gaService.manageColor(diagram, <span class="string"> | |
<em><br> | |
E_CLASS_FOREGROUND</em></span>));<br> | |
style.setBackground(gaService.manageColor(diagram,<br> | |
<span class="string"><em>E_CLASS_BACKGROUND</em></span>));<br> | |
}<br> <span class="keyword">return</span> | |
style;<br> <br> }<br> <br> | |
<span class="keyword"> public static</span> Style getStyleForEClassText(Diagram | |
diagram) {<br> | |
<span class="keyword">final</span> String styleId = <span class="string"> | |
"E-CLASS-TEXT"</span>;<br> IGaService | |
gaService = Graphiti.getGaService();<br><br> | |
<span class="comment">// this is a child style of the common-values-style</span><br> | |
Style parentStyle = <em>getStyleForCommonValues</em>(diagram);<br> | |
Style style = gaService.findStyle(parentStyle, styleId);<br><br> | |
<span class="keyword">if</span> (style == <span class="keyword">null</span>) | |
{ <span class="comment">// style not found - create new style</span><br> | |
style = gaService.createPlainStyle(parentStyle, styleId);<br> | |
<em>setCommonTextValues</em>(diagram, gaService, style);<br> | |
style.setFont(gaService.manageDefaultFont(diagram, <span class="keyword"> | |
false</span>, <span class="keyword">true</span>));<br> | |
}<br> <span class="keyword">return</span> | |
style;<br> }<br><br> | |
<span class="keyword">public static</span> Style getStyleForTextDecorator(Diagram | |
diagram) {<br> | |
<span class="keyword">final</span> String styleId = <span class="string"> | |
"TEXT-DECORATOR-TEXT"</span>;<br> | |
IGaService gaService = Graphiti.getGaService();<br><br> | |
<span class="comment">// this is a child style of the common-values-style</span><br> | |
Style parentStyle = <em>getStyleForCommonValues</em>(diagram);<br> | |
Style style = gaService.findStyle(parentStyle, styleId);<br><br> | |
<span class="keyword">if</span> (style == <span class="keyword">null</span>) | |
{ <span class="comment">// style not found - create new style</span><br> | |
style = gaService.createPlainStyle(parentStyle, styleId);<br> | |
<em>setCommonTextValues</em>(diagram, gaService, style);<br> | |
style.setFont(gaService.manageDefaultFont(diagram, <span class="keyword"> | |
true</span>, <span class="keyword">false</span>));<br> | |
}<br> <span class="keyword">return</span> | |
style;<br> }<br><br> | |
<span class="keyword">private static</span> void setCommonTextValues(Diagram | |
diagram,<br> IGaService gaService, | |
Style style) {<br> style.setFilled(<span class="keyword">false</span>);<br> | |
style.setAngle(0);<br> style.setHorizontalAlignment(Orientation.<span class="string"><em>ALIGNMENT_CENTER</em></span>);<br> | |
style.setVerticalAlignment(Orientation.<span class="string"><em>ALIGNMENT_CENTER</em></span>);<br> | |
style.setForeground(gaService.manageColor(diagram,<br> | |
<span class="string"><em> | |
E_CLASS_TEXT_FOREGROUND</em></span>));<br> }<br><br> | |
<span class="keyword">private static</span> void setCommonValues(Style style) | |
{<br> style.setLineStyle(LineStyle.<span class="string"><em>SOLID</em></span>);<br> | |
style.setLineVisible(<span class="keyword">true</span>);<br> | |
style.setLineWidth(2);<br> style.setTransparency(0.0);<br> | |
}<br>}<br></p> | |
</div> | |
</div> | |
<p> </p> | |
<!-- End code ------------------------------------------------------------------------------- --> | |
<p> </p> | |
<p>Additionally we have to associate the styles to the graphics algorithms in the | |
<span class="inlinecode">add</span>method of the <span class="inlinecode">TutorialAddEClassFeature</span>.</p> | |
<p>We do this exactly at those places, where previously the graphical attributes | |
were set directly on the graphics algorithms.</p> | |
<p>You can see the changed <span class="inlinecode">add</span> method in the following | |
code snippet:</p> | |
<!-- Begin code ------------------------------------------------------------------------------- --> | |
<p> </p> | |
<div class="literallayout"> | |
<div class="incode"> | |
<p class="code"><span class="keyword">public</span> PictogramElement add(IAddContext | |
context) {<br> <br> <span class="comment">// ... | |
EXISTING CODING ...<br></span><br> IGaService gaService | |
= Graphiti.getGaService();<br> <br> | |
RoundedRectangle roundedRectangle; // need to access it later<br> | |
{<br> <span class="comment"> // create | |
invisible outer rectangle expanded by</span><br> | |
<span class="comment"> // the width needed for the anchor</span><br> | |
<span class="keyword">final</span> Rectangle invisibleRectangle =<br> | |
gaService.createInvisibleRectangle(containerShape);<br> | |
gaService.setLocationAndSize(invisibleRectangle, context.getX(),<br> | |
context.getY(), width + <span class="string"><em>INVISIBLE_RECT_RIGHT</em></span>, | |
height);<br><br> <span class="comment"> | |
// create and set visible rectangle inside invisible rectangle</span><br> | |
roundedRectangle =<br> | |
gaService.createPlainRoundedRectangle(invisibleRectangle, 5, 5);<br> | |
roundedRectangle.setStyle(StyleUtil.<em>getStyleForEClass</em>(getDiagram()));<br> | |
gaService.setLocationAndSize(roundedRectangle, 0, 0, width, height);<br> | |
<br> <span class="comment"> // if addedClass | |
has no resource we add it to the resource of the diagram</span><br> | |
<span class="comment"> // in a real scenario the business | |
model would have its own resource</span><br> | |
<span class="keyword"> if</span> (addedClass.eResource() | |
== <span class="keyword">null</span>) {<br> | |
getDiagram().eResource().getContents().add(addedClass);<br> | |
}<br><br> <span class="comment"> // | |
create link and wire it</span><br> | |
link(containerShape, addedClass);<br> }<br> <br> | |
<span class="comment">// SHAPE WITH LINE</span><br> {<br> | |
<span class="comment">// create shape for line</span><br> | |
final Shape shape = peCreateService.createShape(containerShape, | |
<span class="keyword">false</span>);<br><br> | |
<span class="comment">// create and set graphics algorithm</span><br> | |
<span class="keyword">final</span> Polyline polyline =<br> | |
gaService.createPlainPolyline(shape, <span class="keyword">new</span> | |
<span class="keyword">int</span>[] { 0, 20, width, 20 });<br> | |
polyline.setStyle(StyleUtil.<em>getStyleForEClass</em>(getDiagram()));<br> | |
}<br> <br> <span class="comment">// SHAPE WITH TEXT</span><br> | |
{<br> <span class="comment">// | |
create shape for text</span><br> | |
<span class="keyword">final</span> Shape shape = peCreateService.createShape(containerShape, | |
<span class="keyword">false</span>);<br><br> | |
<span class="comment">// create and set text graphics algorithm</span><br> | |
<span class="keyword">final</span> Text text = gaService.createPlainText(shape, | |
addedClass.getName());<br> text.setStyle(StyleUtil.<em>getStyleForEClassText</em>(getDiagram()));<br> | |
gaService.setLocationAndSize(text, 0, 0, width, 20);<br><br> | |
<span class="comment">// create link and wire it</span><br> | |
link(shape, addedClass);<br><br> | |
<span class="comment">// provide information to support direct-editing directly</span><br> | |
<span class="comment">// after object creation (must be activated additionally)</span><br> | |
<span class="keyword">final </span>IDirectEditingInfo directEditingInfo | |
= getFeatureProvider().getDirectEditingInfo();<br> | |
<span class="comment">// set container shape for direct editing after object | |
creation</span><br> directEditingInfo.setMainPictogramElement(containerShape);<br> | |
<span class="comment">// set shape and graphics algorithm where the editor | |
for</span><br> <span class="comment"> | |
// direct editing shall be opened after object creation</span><br> | |
directEditingInfo.setPictogramElement(shape);<br> | |
directEditingInfo.setGraphicsAlgorithm(text);<br> }<br> <br> | |
<span class="comment">// add a chopbox anchor to the shape<br><br> | |
// ... EXISTING CODING ...</span><br><br> ellipse.setStyle(StyleUtil.<em>getStyleForEClass</em>(getDiagram()));<br> | |
<br> <span class="comment">// ... EXISTING CODING ...</span><br> <br> | |
<span class="keyword">return</span> containerShape;<br>}<br></p> | |
</div> | |
</div> | |
<p> </p> | |
<!-- End code ------------------------------------------------------------------------------- --> | |
<p>Tto achieve a homogeneously appearence of our editor, the styles must also be | |
applied to our EReferences. To do this we have to change the | |
<span class="inlinecode">add</span>- and <span class="inlinecode">createArrow</span>-method | |
in class <span class="inlinecode">TutorialAddEReferenceFeature</span>.</p> | |
<!-- Begin code ------------------------------------------------------------------------------- --> | |
<p> </p> | |
<div class="literallayout"> | |
<div class="incode"> | |
<p class="code"><span class="keyword">package</span> org.eclipse.graphiti.examples.tutorial.features;<br> | |
<br><span class="keyword">public class</span> TutorialAddEReferenceFeature | |
<span class="keyword">extends</span> AbstractAddFeature {<br><br> | |
<span class="comment"> // ... EXISTING CODING ...</span><br> | |
<br><span class="keyword"> public</span> PictogramElement | |
add(IAddContext context) {<br> | |
IAddConnectionContext addConContext =<br> | |
(IAddConnectionContext) context;<br> | |
EReference addedEReference =<br> | |
(EReference) context.getNewObject();<br> | |
IPeCreateService peCreateService =<br> | |
Graphiti.getPeCreateService();<br><br> | |
<span class="comment">// CONNECTION WITH POLYLINE</span><br> | |
Connection connection =<br> | |
peCreateService.createFreeFormConnection(getDiagram());<br> | |
connection.setStart(addConContext.getSourceAnchor());<br> | |
connection.setEnd(addConContext.getTargetAnchor());<br><br> | |
IGaService gaService = Graphiti.getGaService();<br> | |
Polyline polyline = gaService.createPlainPolyline(connection);<br> | |
polyline.setStyle(StyleUtil.<em>getStyleForEClass</em>(getDiagram()));<br> | |
<br> <span class="comment">// create | |
link and wire it</span><br> link(connection, | |
addedEReference);<br><br> | |
<span class="comment">// add dynamic text decorator for the reference name</span><br> | |
ConnectionDecorator textDecorator =<br> | |
peCreateService.createConnectionDecorator(connection,<br> | |
true, 0.5, true);<br> Text text = gaService.createPlainText(textDecorator);<br> | |
text.setStyle(StyleUtil.<em>getStyleForTextDecorator</em>((getDiagram())));<br> | |
gaService.setLocation(text, 10, 0);<br><br> | |
<span class="comment">// set reference name in the text decorator</span><br> | |
EReference eReference = (EReference) context.getNewObject();<br> | |
text.setValue(eReference.getName());<br><br> | |
<span class="comment">// add static graphical decorators (composition and | |
navigable)</span><br> ConnectionDecorator | |
cd;<br> cd = peCreateService.createConnectionDecorator(connection,<br> | |
<span class="keyword">false</span>, 1.0, true);<br> | |
createArrow(cd);<br> | |
<span class="keyword">return</span> connection;<br> }<br> | |
<br><span class="comment"> // ... EXISTING CODING ...</span><br> | |
<br> <span class="keyword">private</span> Polyline createArrow(GraphicsAlgorithmContainer | |
gaContainer) {<br> Polyline polyline | |
=<br> | |
Graphiti.getGaCreateService().createPlainPolyline(gaContainer,<br> | |
<span class="keyword">new int</span>[] { -15, 10, 0, 0, -15, -10 });<br> | |
polyline.setStyle(StyleUtil.<em>getStyleForEClass</em>(getDiagram()));<br> | |
<span class="keyword">return</span> polyline;<br> }<br> | |
}</p> | |
</div> | |
</div> | |
<p> </p> | |
<!-- End code ------------------------------------------------------------------------------- --> | |
<p>To test the just implemented styles we have to create a small | |
<a href="custom-feature.htm">custom feature</a>, which allows changing the foreground | |
color of the style.</p> | |
<p>The implementation can be seen here:</p> | |
<!-- Begin code ------------------------------------------------------------------------------- --> | |
<p> </p> | |
<div class="literallayout"> | |
<div class="incode"> | |
<p class="code"><span class="keyword">package</span> org.eclipse.graphiti.examples.tutorial.features;<br> <br> | |
<span class="keyword">public class</span> TutorialChangeColorEClassFeature | |
<span class="keyword">extends</span> AbstractCustomFeature {<br> <br> | |
<span class="keyword">public</span> TutorialChangeColorEClassFeature(IFeatureProvider | |
fp) {<br> | |
<span class="keyword">super</span>(fp);<br> }<br><br> | |
@Override<br> <span class="keyword">public</span> String | |
getName() {<br> | |
<span class="keyword">return</span> <span class="string">"Change &foreground | |
color"</span>;<br> }<br><br> @Override<br> | |
<span class="keyword">public</span> String getDescription() {<br> | |
<span class="keyword">return</span> <span class="string">"Change the foreground | |
color"</span>;<br> }<br><br> @Override<br> | |
<span class="keyword">public boolean</span> canExecute(ICustomContext context) | |
{<br> PictogramElement[] pes = | |
context.getPictogramElements();<br> | |
<span class="keyword">if</span> (pes == <span class="keyword">null</span> | |
|| pes.<span class="string">length</span> == 0) { <span class="comment"> | |
// nothing selected</span><br> | |
<span class="keyword"> return false</span>;<br> | |
}<br> <span class="comment">// | |
return true, if all elements are EClasses<br> | |
// note, that in execute() the selected elements are not even accessed,<br> | |
// so theoretically it would be possible that canExecute() always<br> | |
// returns true. But for usability reasons it is better to check<br> | |
// if the selected elements are EClasses.</span><br> | |
<span class="keyword">for</span> (PictogramElement pe : pes) {<br> | |
<span class="keyword">final</span> Object bo = getBusinessObjectForPictogramElement(pe);<br> | |
if (!(bo <span class="keyword">instanceof</span> EClass)) {<br> | |
<span class="keyword">return false</span>;<br> | |
}<br> }<br> | |
<span class="keyword">return true</span>;<br> }<br> | |
<br> <span class="keyword">public</span> void execute(ICustomContext | |
context) {<br> Style style = StyleUtil.getStyleForEClass(getDiagram());<br> | |
<br> <span class="comment">// | |
let the user choose the new color</span><br> | |
Color currentColor = style.getForeground();<br> | |
Color newColor = ExampleUtil.<em>editColor</em>(currentColor);<br> | |
<span class="keyword">if</span> (newColor == <span class="keyword">null</span>) | |
{ <span class="comment">// user did not choose new color</span><br> | |
<span class="keyword">return</span>;<br> | |
}<br> style.setForeground(newColor);<br> | |
}<br>}<br></p> | |
</div> | |
</div> | |
<p> </p> | |
<!-- End code ------------------------------------------------------------------------------- --> | |
<p>Finally the feature provider has to deliver our newly created custom feature | |
(overwrite the method | |
<a href="../../../javadoc/org/eclipse/graphiti/features/IFeatureProvider.html#getCustomFeatures(org.eclipse.graphiti.features.context.ICustomContext)"> | |
getCustomFeatures</a>).</p> | |
<p>This implementation can be seen here:</p> | |
<!-- Begin code ------------------------------------------------------------------------------- --> | |
<p> </p> | |
<div class="literallayout"> | |
<div class="incode"> | |
<p class="code">@Override<br><span class="keyword">public</span> ICustomFeature[] | |
getCustomFeatures(ICustomContext context) {<br> | |
<span class="keyword">return new</span> ICustomFeature[] { | |
<span class="keyword">new</span> TutorialRenameEClassFeature(<span class="keyword">this</span>),<br> | |
<span class="keyword">new</span> TutorialDrillDownEClassFeature(<span class="keyword">this</span>),<br> | |
<span class="keyword">new</span> TutorialAssociateDiagramEClassFeature(<span class="keyword">this</span>),<br> | |
<span class="keyword">new</span> TutorialCollapseDummyFeature(<span class="keyword">this</span>),<br> | |
<span class="keyword">new</span> TutorialChangeColorEClassFeature(<span class="keyword">this</span>)};<br>}<br></p> | |
</div> | |
</div> | |
<p> </p> | |
<!-- End code ------------------------------------------------------------------------------- --> | |
<h2>Test: Change background color of all EClasses</h2> | |
<p>Now start the editor and test this:</p> | |
<ol> | |
<li>create or open a new diagram</li> | |
<li>create several new EClasses and EReferences</li> | |
<li>open the context menu on one of the EClasses; choose "Change Foreground | |
color"</li> | |
<li>verify that the color of all EClasses is changed accordingly</li> | |
</ol> | |
<p> </p> | |
</body> | |
</html> |