@@ -1266,6 +1266,58 @@ private int getParagraphOffset(int parIdx) {
12661266 return position (parIdx , 0 ).toOffset ();
12671267 }
12681268
1269+ /**
1270+ * Returns the bounds of the paragraph if it is visible or {@link Optional#empty()} if it's not.
1271+ *
1272+ * The returned bounds object will always be within the bounds of the area. In other words, it takes
1273+ * scrolling into account. Note: the bound's width will always equal the area's width, not necessarily
1274+ * the paragraph's real width (if it's short and doesn't take up all of the area's provided horizontal space
1275+ * or if it's long and spans outside of the area's width).
1276+ *
1277+ * @param visibleParagraphIndex the index in area's list of visible paragraphs.
1278+ */
1279+ public Bounds getVisibleParagraphBoundsOnScreen (int visibleParagraphIndex ) {
1280+ return getParagraphBoundsOnScreen (virtualFlow .visibleCells ().get (visibleParagraphIndex ));
1281+ }
1282+
1283+ /**
1284+ * Returns the bounds of the paragraph if it is visible or {@link Optional#empty()} if it's not.
1285+ *
1286+ * The returned bounds object will always be within the bounds of the area. In other words, it takes
1287+ * scrolling into account. Note: the bound's width will always equal the area's width, not necessarily
1288+ * the paragraph's real width (if it's short and doesn't take up all of the area's provided horizontal space
1289+ * or if it's long and spans outside of the area's width).
1290+ *
1291+ * @param paragraphIndex the index in area's list of paragraphs (visible and invisible).
1292+ */
1293+ public Optional <Bounds > getParagraphBoundsOnScreen (int paragraphIndex ) {
1294+ return virtualFlow .getCellIfVisible (paragraphIndex ).map (this ::getParagraphBoundsOnScreen );
1295+ }
1296+
1297+ private Bounds getParagraphBoundsOnScreen (Cell <Paragraph <PS , SEG , S >, ParagraphBox <PS , SEG , S >> cell ) {
1298+ Bounds nodeLocal = cell .getNode ().getBoundsInLocal ();
1299+ Bounds nodeScreen = cell .getNode ().localToScreen (nodeLocal );
1300+ Bounds areaLocal = getBoundsInLocal ();
1301+ Bounds areaScreen = localToScreen (areaLocal );
1302+
1303+ // use area's minX if scrolled right and paragraph's left is not visible
1304+ double minX = nodeScreen .getMinX () < areaScreen .getMinX ()
1305+ ? areaScreen .getMinX ()
1306+ : nodeScreen .getMinX ();
1307+ // use area's minY if scrolled down vertically and paragraph's top is not visible
1308+ double minY = nodeScreen .getMinY () < areaScreen .getMinY ()
1309+ ? areaScreen .getMinY ()
1310+ : nodeScreen .getMinY ();
1311+ // use area's width whether paragraph spans outside of it or not
1312+ // so that short or long paragraph takes up the entire space
1313+ double width = areaScreen .getWidth ();
1314+ // use area's maxY if scrolled up vertically and paragraph's bottom is not visible
1315+ double maxY = nodeScreen .getMaxY () < areaScreen .getMaxY ()
1316+ ? nodeScreen .getMaxY ()
1317+ : areaScreen .getMaxY ();
1318+ return new BoundingBox (minX , minY , width , maxY - minY );
1319+ }
1320+
12691321 private Optional <Bounds > getRangeBoundsOnScreen (int paragraphIndex , int from , int to ) {
12701322 return virtualFlow .getCellIfVisible (paragraphIndex )
12711323 .map (c -> c .getNode ().getRangeBoundsOnScreen (from , to ));
0 commit comments