Skip to content

Commit c5d3b2e

Browse files
committed
Implemented the new inside label alignment values.
- Added left-inside / right-inside for text-halign - Added top-inside / bottom-inside for text-valign - Updated label projection, bounds, canvas drawing, texture offsets, and edge endpoint label intersection logic - Updated docs and TypeScript definitions - Added tests for style acceptance and label anchor math Ref: Inside-shape alignment of text labels #3443
1 parent 38cad00 commit c5d3b2e

11 files changed

Lines changed: 192 additions & 26 deletions

File tree

documentation/md/style.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -695,8 +695,8 @@ Wrapping text:
695695
696696
Node label alignment:
697697
698-
* **`text-halign`** : The horizontal alignment of a node's label; may have value `left`, `center`, or `right`.
699-
* **`text-valign`** : The vertical alignment of a node's label; may have value `top`, `center`, or `bottom`.
698+
* **`text-halign`** : The horizontal alignment of a node's label; may have value `left`, `left-inside`, `center`, `right`, or `right-inside`.
699+
* **`text-valign`** : The vertical alignment of a node's label; may have value `top`, `top-inside`, `center`, `bottom`, or `bottom-inside`.
700700
701701
Edge label alignment:
702702

index.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5598,11 +5598,11 @@ declare namespace cytoscape {
55985598
/**
55995599
* The horizontal alignment of a node’s label.
56005600
*/
5601-
"text-halign": PropertyValue<SingularType, "left" | "center" | "right">;
5601+
"text-halign": PropertyValue<SingularType, "left" | "left-inside" | "center" | "right" | "right-inside">;
56025602
/**
56035603
* The vertical alignment of a node’s label.
56045604
*/
5605-
"text-valign": PropertyValue<SingularType, "top" | "center" | "bottom">;
5605+
"text-valign": PropertyValue<SingularType, "top" | "top-inside" | "center" | "bottom" | "bottom-inside">;
56065606

56075607
/**
56085608
* Edge label alignment:

src/collection/dimensions/bounds.mjs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as is from '../../is.mjs';
22
import { assignBoundingBox, expandBoundingBoxSides, clearBoundingBox, expandBoundingBox, makeBoundingBox, copyBoundingBox, shiftBoundingBox, updateBoundingBox } from '../../math.mjs';
33
import {defaults, endsWith, getPrefixedProperty, hashIntsArray, memoize} from '../../util/index.mjs';
4+
import { labelHalign, labelValign } from '../../style/align.mjs';
45

56
let fn, elesfn;
67

@@ -305,7 +306,7 @@ let updateBoundsFromLabel = function( bounds, ele, prefix ){
305306
ly1 = labelY - lh_2;
306307
ly2 = labelY + lh_2;
307308
} else {
308-
switch( halign.value ){
309+
switch( labelHalign( halign.value ) ){
309310
case 'left':
310311
lx1 = labelX - lw;
311312
lx2 = labelX;
@@ -322,7 +323,7 @@ let updateBoundsFromLabel = function( bounds, ele, prefix ){
322323
break;
323324
}
324325

325-
switch( valign.value ){
326+
switch( labelValign( valign.value ) ){
326327
case 'top':
327328
ly1 = labelY - lh;
328329
ly2 = labelY;
@@ -379,7 +380,7 @@ let updateBoundsFromLabel = function( bounds, ele, prefix ){
379380
let yo = (ly1 + ly2)/2;
380381

381382
if( !isEdge ){
382-
switch( halign.value ){
383+
switch( labelHalign( halign.value ) ){
383384
case 'left':
384385
xo = lx2;
385386
break;
@@ -389,7 +390,7 @@ let updateBoundsFromLabel = function( bounds, ele, prefix ){
389390
break;
390391
}
391392

392-
switch( valign.value ){
393+
switch( labelValign( valign.value ) ){
393394
case 'top':
394395
yo = ly2;
395396
break;

src/extensions/renderer/base/coord-ele-math/edge-endpoints.mjs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as math from '../../../../math.mjs';
22
import * as is from '../../../../is.mjs';
33
import {endsWith} from "../../../../util/index.mjs";
4+
import { labelHalign, labelValign } from '../../../../style/align.mjs';
45

56
let BRp = {};
67

@@ -155,13 +156,15 @@ BRp.findEndpoints = function( edge ){
155156
let lh2 = lh/2;
156157

157158
let va = target.pstyle('text-valign').value;
159+
va = labelValign( va );
158160
if( va === 'top' ){
159161
ly -= lh2;
160162
} else if( va === 'bottom' ){
161163
ly += lh2;
162164
}
163165

164166
let ha = target.pstyle('text-halign').value;
167+
ha = labelHalign( ha );
165168
if( ha === 'left' ){
166169
lx -= lw2;
167170
} else if( ha === 'right' ){
@@ -247,13 +250,15 @@ BRp.findEndpoints = function( edge ){
247250
let lh2 = lh/2;
248251

249252
let va = source.pstyle('text-valign').value;
253+
va = labelValign( va );
250254
if( va === 'top' ){
251255
ly -= lh2;
252256
} else if( va === 'bottom' ){
253257
ly += lh2;
254258
}
255259

256260
let ha = source.pstyle('text-halign').value;
261+
ha = labelHalign( ha );
257262
if( ha === 'left' ){
258263
lx -= lw2;
259264
} else if( ha === 'right' ){

src/extensions/renderer/base/coord-ele-math/labels.mjs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as math from '../../../../math.mjs';
22
import * as is from '../../../../is.mjs';
33
import * as util from '../../../../util/index.mjs';
4+
import { labelJustification } from '../../../../style/align.mjs';
45

56
let BRp = {};
67

@@ -25,10 +26,18 @@ BRp.recalculateNodeLabelProjection = function( node ){
2526
textX = nodePos.x - nodeWidth / 2 - padding;
2627
break;
2728

29+
case 'left-inside':
30+
textX = nodePos.x - nodeWidth / 2 + padding;
31+
break;
32+
2833
case 'right':
2934
textX = nodePos.x + nodeWidth / 2 + padding;
3035
break;
3136

37+
case 'right-inside':
38+
textX = nodePos.x + nodeWidth / 2 - padding;
39+
break;
40+
3241
default: // e.g. center
3342
textX = nodePos.x;
3443
}
@@ -38,10 +47,18 @@ BRp.recalculateNodeLabelProjection = function( node ){
3847
textY = nodePos.y - nodeHeight / 2 - padding;
3948
break;
4049

50+
case 'top-inside':
51+
textY = nodePos.y - nodeHeight / 2 + padding;
52+
break;
53+
4154
case 'bottom':
4255
textY = nodePos.y + nodeHeight / 2 + padding;
4356
break;
4457

58+
case 'bottom-inside':
59+
textY = nodePos.y + nodeHeight / 2 - padding;
60+
break;
61+
4562
default: // e.g. middle
4663
textY = nodePos.y;
4764
}
@@ -459,14 +476,7 @@ BRp.getLabelJustification = function(ele){
459476

460477
if( justification === 'auto' ){
461478
if( ele.isNode() ){
462-
switch( textHalign ){
463-
case 'left':
464-
return 'right';
465-
case 'right':
466-
return 'left';
467-
default:
468-
return 'center';
469-
}
479+
return labelJustification( textHalign );
470480
} else {
471481
return 'center';
472482
}

src/extensions/renderer/canvas/drawing-label-text.mjs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as util from '../../../util/index.mjs';
22
import * as math from '../../../math.mjs';
3+
import { labelHalign, labelValign } from '../../../style/align.mjs';
34

45
let CRp = {};
56

@@ -233,7 +234,10 @@ CRp.drawText = function( context, ele, prefix, applyRotation = true, useEleOpaci
233234
textY = 0;
234235
}
235236

236-
switch( valign ){
237+
let boxHalign = labelHalign( halign );
238+
let boxValign = labelValign( valign );
239+
240+
switch( boxValign ){
237241
case 'top':
238242
break;
239243
case 'center':
@@ -266,7 +270,7 @@ CRp.drawText = function( context, ele, prefix, applyRotation = true, useEleOpaci
266270
let doStroke = textBorderWidth > 0 && borderOpacity > 0;
267271

268272
let bgX = textX - backgroundPadding;
269-
switch( halign ){
273+
switch( boxHalign ){
270274
case 'left': bgX -= textW; break;
271275
case 'center': bgX -= textW / 2; break;
272276
}
@@ -344,27 +348,27 @@ CRp.drawText = function( context, ele, prefix, applyRotation = true, useEleOpaci
344348

345349
if( justification === 'auto' ){
346350
// then it's already ok, so skip all the other ifs
347-
} else if( halign === 'left' ){ // auto justification : right
351+
} else if( boxHalign === 'left' ){ // auto justification : right
348352
if( justification === 'left' ){
349353
textX += -textW;
350354
} else if( justification === 'center' ){
351355
textX += -halfTextW;
352356
} // else same as auto
353-
} else if( halign === 'center' ){ // auto justfication : center
357+
} else if( boxHalign === 'center' ){ // auto justfication : center
354358
if( justification === 'left' ){
355359
textX += -halfTextW;
356360
} else if( justification === 'right' ){
357361
textX += halfTextW;
358362
} // else same as auto
359-
} else if( halign === 'right' ){ // auto justification : left
363+
} else if( boxHalign === 'right' ){ // auto justification : left
360364
if( justification === 'center' ){
361365
textX += halfTextW;
362366
} else if( justification === 'right' ){
363367
textX += textW;
364368
} // else same as auto
365369
}
366370

367-
switch( valign ){
371+
switch( boxValign ){
368372
case 'top':
369373
textY -= ( lines.length - 1 ) * lineHeight;
370374
break;

src/extensions/renderer/canvas/index.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Modifications tracked on Github.
99
import * as util from '../../../util/index.mjs';
1010
import * as is from '../../../is.mjs';
1111
import { makeBoundingBox } from '../../../math.mjs';
12+
import { labelHalign, labelValign } from '../../../style/align.mjs';
1213
import ElementTextureCache from './ele-texture-cache.mjs';
1314
import LayeredTextureCache from './layered-texture-cache.mjs';
1415

@@ -186,7 +187,7 @@ function CanvasRenderer( options ){
186187
let p = getCenterOffset( getLabelBox(ele) );
187188

188189
if( ele.isNode() ){
189-
switch( ele.pstyle('text-halign').value ){
190+
switch( labelHalign( ele.pstyle('text-halign').value ) ){
190191
case 'left':
191192
p.x = -bb.w - (bb.leftPad || 0);
192193
break;
@@ -195,7 +196,7 @@ function CanvasRenderer( options ){
195196
break;
196197
}
197198

198-
switch( ele.pstyle('text-valign').value ){
199+
switch( labelValign( ele.pstyle('text-valign').value ) ){
199200
case 'top':
200201
p.y = -bb.h - (bb.topPad || 0);
201202
break;

src/style/align.mjs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
export const labelHalign = halign => {
2+
switch( halign ){
3+
case 'left':
4+
case 'right-inside':
5+
return 'left';
6+
7+
case 'right':
8+
case 'left-inside':
9+
return 'right';
10+
11+
default:
12+
return 'center';
13+
}
14+
};
15+
16+
export const labelValign = valign => {
17+
switch( valign ){
18+
case 'top':
19+
case 'bottom-inside':
20+
return 'top';
21+
22+
case 'bottom':
23+
case 'top-inside':
24+
return 'bottom';
25+
26+
default:
27+
return 'center';
28+
}
29+
};
30+
31+
export const labelJustification = halign => {
32+
switch( halign ){
33+
case 'left':
34+
return 'right';
35+
36+
case 'right':
37+
return 'left';
38+
39+
case 'left-inside':
40+
return 'left';
41+
42+
case 'right-inside':
43+
return 'right';
44+
45+
default:
46+
return 'center';
47+
}
48+
};

src/style/properties.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ const styfn = {};
8888
visibility: { enums: [ 'hidden', 'visible' ] },
8989
zCompoundDepth: { enums: [ 'bottom', 'orphan', 'auto', 'top' ] },
9090
zIndexCompare: { enums: [ 'auto', 'manual' ] },
91-
valign: { enums: [ 'top', 'center', 'bottom' ] },
92-
halign: { enums: [ 'left', 'center', 'right' ] },
91+
valign: { enums: [ 'top', 'top-inside', 'center', 'bottom', 'bottom-inside' ] },
92+
halign: { enums: [ 'left', 'left-inside', 'center', 'right', 'right-inside' ] },
9393
justification: { enums: [ 'left', 'center', 'right', 'auto' ] },
9494
text: { string: true },
9595
data: { mapping: true, regex: data( 'data' ) },

test/collection-style.mjs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,26 @@ describe('Collection style', function(){
197197
expect( cy.$('#n1').style('width') ).to.equal('20px');
198198
});
199199

200+
it('ele.style() accepts inside label alignment values', function(){
201+
var n1 = cy.$('#n1');
202+
203+
n1.style({
204+
'text-halign': 'left-inside',
205+
'text-valign': 'top-inside'
206+
});
207+
208+
expect( n1.style('text-halign') ).to.equal('left-inside');
209+
expect( n1.style('text-valign') ).to.equal('top-inside');
210+
211+
n1.style({
212+
'text-halign': 'right-inside',
213+
'text-valign': 'bottom-inside'
214+
});
215+
216+
expect( n1.style('text-halign') ).to.equal('right-inside');
217+
expect( n1.style('text-valign') ).to.equal('bottom-inside');
218+
});
219+
200220
it('ele.style(propName) works for unitless property value', function(){
201221
expect( cy.$('#n1').style('opacity') ).to.equal('0.5');
202222
});

0 commit comments

Comments
 (0)