From e3931ba61926bf03c32368a218d59b9d76ac8844 Mon Sep 17 00:00:00 2001 From: Shashank Budhanuru Ramaraju Date: Thu, 24 Aug 2023 10:44:27 +0100 Subject: [PATCH] Add glyph tooltip --- .../LinearApolloDisplay/glyphs/BoxGlyph.ts | 72 +--------------- .../glyphs/CanonicalGeneGlyph.ts | 68 --------------- .../glyphs/GenericChildGlyph.ts | 68 --------------- .../src/LinearApolloDisplay/glyphs/Glyph.ts | 84 +++++++++++++++++-- .../glyphs/ImplicitExonGeneGlyph.ts | 68 --------------- .../stateModel/rendering.ts | 6 +- 6 files changed, 85 insertions(+), 281 deletions(-) diff --git a/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/BoxGlyph.ts b/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/BoxGlyph.ts index a16291212..1ae436750 100644 --- a/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +++ b/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/BoxGlyph.ts @@ -3,10 +3,7 @@ import { AnnotationFeatureI } from 'apollo-mst' import { LocationEndChange, LocationStartChange } from 'apollo-shared' import { LinearApolloDisplay } from '../stateModel' -import { - LinearApolloDisplayMouseEvents, - MousePosition, -} from '../stateModel/mouseEvents' +import { MousePosition } from '../stateModel/mouseEvents' import { CanvasMouseEvent } from '../types' import { Glyph } from './Glyph' @@ -15,73 +12,6 @@ export class BoxGlyph extends Glyph { return 1 } - drawTooltip( - linearApolloDisplayMouseEvents: LinearApolloDisplayMouseEvents, - context: CanvasRenderingContext2D, - ) { - const { apolloHover, lgv, apolloRowHeight, displayedRegions } = - linearApolloDisplayMouseEvents - if (!apolloHover) { - return - } - const { feature, mousePosition } = apolloHover - if (!(feature && mousePosition)) { - return - } - const { regionNumber, y } = mousePosition - const displayedRegion = displayedRegions[regionNumber] - const { refName, reversed } = displayedRegion - const { bpPerPx, bpToPx, offsetPx } = lgv - const { start, end, length } = feature - let startPx = - (bpToPx({ refName, coord: reversed ? end : start, regionNumber }) - ?.offsetPx ?? 0) - offsetPx - const row = Math.floor(y / apolloRowHeight) - const top = row * apolloRowHeight - const widthPx = length / bpPerPx - - const featureType = `Type: ${feature.type}` - const featureName = feature.attributes - .get('gff_name') - ?.find((name) => name !== '') - const featureStart = `Start: ${feature.start.toString()}` - const featureEnd = `End: ${feature.end.toString()}` - const textWidth = [ - context.measureText(featureType).width, - context.measureText(featureStart).width, - context.measureText(featureEnd).width, - ] - if (featureName) { - textWidth.push(context.measureText(`Name: ${featureName}`).width) - } - const maxWidth = Math.max(...textWidth) - - startPx = startPx + widthPx + 5 - context.fillStyle = 'rgba(1, 1, 1, 0.7)' - context.fillRect( - startPx, - top, - maxWidth + 4, - textWidth.length === 4 ? 55 : 45, - ) - context.beginPath() - context.moveTo(startPx, top) - context.lineTo(startPx - 5, top + 5) - context.lineTo(startPx, top + 10) - context.fill() - context.fillStyle = 'rgba(255, 255, 255)' - let textTop = top + 12 - context.fillText(featureType, startPx + 2, textTop) - if (featureName) { - textTop = textTop + 12 - context.fillText(`Name: ${featureName}`, startPx + 2, textTop) - } - textTop = textTop + 12 - context.fillText(featureStart, startPx + 2, textTop) - textTop = textTop + 12 - context.fillText(featureEnd, startPx + 2, textTop) - } - draw( stateModel: LinearApolloDisplay, ctx: CanvasRenderingContext2D, diff --git a/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/CanonicalGeneGlyph.ts b/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/CanonicalGeneGlyph.ts index 690c664dc..2527dccdc 100644 --- a/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/CanonicalGeneGlyph.ts +++ b/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/CanonicalGeneGlyph.ts @@ -1,7 +1,6 @@ import { AnnotationFeatureI } from 'apollo-mst' import { LinearApolloDisplay } from '../stateModel' -import { LinearApolloDisplayMouseEvents } from '../stateModel/mouseEvents' import { CanvasMouseEvent } from '../types' import { Glyph } from './Glyph' @@ -111,73 +110,6 @@ export class CanonicalGeneGlyph extends Glyph { return cdsCount } - drawTooltip( - linearApolloDisplayMouseEvents: LinearApolloDisplayMouseEvents, - context: CanvasRenderingContext2D, - ) { - const { apolloHover, lgv, apolloRowHeight, displayedRegions } = - linearApolloDisplayMouseEvents - if (!apolloHover) { - return - } - const { feature, mousePosition } = apolloHover - if (!(feature && mousePosition)) { - return - } - const { regionNumber, y } = mousePosition - const displayedRegion = displayedRegions[regionNumber] - const { refName, reversed } = displayedRegion - const { bpPerPx, bpToPx, offsetPx } = lgv - const { start, end, length } = feature - let startPx = - (bpToPx({ refName, coord: reversed ? end : start, regionNumber }) - ?.offsetPx ?? 0) - offsetPx - const row = Math.floor(y / apolloRowHeight) - const top = row * apolloRowHeight - const widthPx = length / bpPerPx - - const featureType = `Type: ${feature.type}` - const featureName = feature.attributes - .get('gff_name') - ?.find((name) => name !== '') - const featureStart = `Start: ${feature.start.toString()}` - const featureEnd = `End: ${feature.end.toString()}` - const textWidth = [ - context.measureText(featureType).width, - context.measureText(featureStart).width, - context.measureText(featureEnd).width, - ] - if (featureName) { - textWidth.push(context.measureText(`Name: ${featureName}`).width) - } - const maxWidth = Math.max(...textWidth) - - startPx = startPx + widthPx + 5 - context.fillStyle = 'rgba(1, 1, 1, 0.7)' - context.fillRect( - startPx, - top, - maxWidth + 4, - textWidth.length === 4 ? 55 : 45, - ) - context.beginPath() - context.moveTo(startPx, top) - context.lineTo(startPx - 5, top + 5) - context.lineTo(startPx, top + 10) - context.fill() - context.fillStyle = 'rgba(255, 255, 255)' - let textTop = top + 12 - context.fillText(featureType, startPx + 2, textTop) - if (featureName) { - textTop = textTop + 12 - context.fillText(`Name: ${featureName}`, startPx + 2, textTop) - } - textTop = textTop + 12 - context.fillText(featureStart, startPx + 2, textTop) - textTop = textTop + 12 - context.fillText(featureEnd, startPx + 2, textTop) - } - draw( stateModel: LinearApolloDisplay, ctx: CanvasRenderingContext2D, diff --git a/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts b/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts index b7cf5ebb0..458b11c35 100644 --- a/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +++ b/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts @@ -1,7 +1,6 @@ import { AnnotationFeatureI } from 'apollo-mst' import { LinearApolloDisplay } from '../stateModel' -import { LinearApolloDisplayMouseEvents } from '../stateModel/mouseEvents' import { CanvasMouseEvent } from '../types' import { Glyph } from './Glyph' @@ -20,73 +19,6 @@ export class GenericChildGlyph extends Glyph { return this.featuresForRow(feature).length } - drawTooltip( - linearApolloDisplayMouseEvents: LinearApolloDisplayMouseEvents, - context: CanvasRenderingContext2D, - ) { - const { apolloHover, lgv, apolloRowHeight, displayedRegions } = - linearApolloDisplayMouseEvents - if (!apolloHover) { - return - } - const { feature, mousePosition } = apolloHover - if (!(feature && mousePosition)) { - return - } - const { regionNumber, y } = mousePosition - const displayedRegion = displayedRegions[regionNumber] - const { refName, reversed } = displayedRegion - const { bpPerPx, bpToPx, offsetPx } = lgv - const { start, end, length } = feature - let startPx = - (bpToPx({ refName, coord: reversed ? end : start, regionNumber }) - ?.offsetPx ?? 0) - offsetPx - const row = Math.floor(y / apolloRowHeight) - const top = row * apolloRowHeight - const widthPx = length / bpPerPx - - const featureType = `Type: ${feature.type}` - const featureName = feature.attributes - .get('gff_name') - ?.find((name) => name !== '') - const featureStart = `Start: ${feature.start.toString()}` - const featureEnd = `End: ${feature.end.toString()}` - const textWidth = [ - context.measureText(featureType).width, - context.measureText(featureStart).width, - context.measureText(featureEnd).width, - ] - if (featureName) { - textWidth.push(context.measureText(`Name: ${featureName}`).width) - } - const maxWidth = Math.max(...textWidth) - - startPx = startPx + widthPx + 5 - context.fillStyle = 'rgba(1, 1, 1, 0.7)' - context.fillRect( - startPx, - top, - maxWidth + 4, - textWidth.length === 4 ? 55 : 45, - ) - context.beginPath() - context.moveTo(startPx, top) - context.lineTo(startPx - 5, top + 5) - context.lineTo(startPx, top + 10) - context.fill() - context.fillStyle = 'rgba(255, 255, 255)' - let textTop = top + 12 - context.fillText(featureType, startPx + 2, textTop) - if (featureName) { - textTop = textTop + 12 - context.fillText(`Name: ${featureName}`, startPx + 2, textTop) - } - textTop = textTop + 12 - context.fillText(featureStart, startPx + 2, textTop) - textTop = textTop + 12 - context.fillText(featureEnd, startPx + 2, textTop) - } - draw( stateModel: LinearApolloDisplay, ctx: CanvasRenderingContext2D, diff --git a/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/Glyph.ts b/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/Glyph.ts index d4ea19a68..1c8526ce0 100644 --- a/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/Glyph.ts +++ b/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/Glyph.ts @@ -32,11 +32,6 @@ export abstract class Glyph { row: number, ): AnnotationFeatureI | undefined - abstract drawTooltip( - display: LinearApolloDisplayMouseEvents, - context: CanvasRenderingContext2D, - ): void - drawHover( _display: LinearApolloDisplayMouseEvents, _overlayCtx: CanvasRenderingContext2D, @@ -104,6 +99,85 @@ export abstract class Glyph { return } + drawTooltip( + display: LinearApolloDisplayMouseEvents, + context: CanvasRenderingContext2D, + ): void { + const { apolloHover, apolloRowHeight, displayedRegions, lgv } = display + if (!apolloHover) { + return + } + const { feature, mousePosition } = apolloHover + if (!(feature && mousePosition)) { + return + } + const { regionNumber, y } = mousePosition + const displayedRegion = displayedRegions[regionNumber] + const { refName, reversed } = displayedRegion + const { bpPerPx, bpToPx, offsetPx } = lgv + + const { discontinuousLocations } = feature + let start: number, end: number, length: number + if (discontinuousLocations && discontinuousLocations.length > 0) { + const lastLoc = discontinuousLocations.at(-1) + if (!lastLoc) { + return + } + start = lastLoc?.start + end = lastLoc?.end + length = lastLoc?.end - lastLoc?.start + } else { + ;({ end, length, start } = feature) + } + + let startPx = + (bpToPx({ refName, coord: reversed ? end : start, regionNumber }) + ?.offsetPx ?? 0) - offsetPx + const row = Math.floor(y / apolloRowHeight) + const top = row * apolloRowHeight + const widthPx = length / bpPerPx + + const featureType = `Type: ${feature.type}` + const { attributes } = feature + const featureName = attributes.get('gff_name')?.find((name) => name !== '') + const featureStart = `Start: ${feature.start.toString()}` + const featureEnd = `End: ${feature.end.toString()}` + const textWidth = [ + context.measureText(featureType).width, + context.measureText(featureStart).width, + context.measureText(featureEnd).width, + ] + if (featureName) { + textWidth.push(context.measureText(`Name: ${featureName}`).width) + } + const maxWidth = Math.max(...textWidth) + + startPx = startPx + widthPx + 5 + context.fillStyle = 'rgba(1, 1, 1, 0.7)' + context.fillRect( + startPx, + top, + maxWidth + 4, + textWidth.length === 4 ? 55 : 45, + ) + context.beginPath() + context.moveTo(startPx, top) + context.lineTo(startPx - 5, top + 5) + context.lineTo(startPx, top + 10) + context.fill() + context.fillStyle = 'rgba(255, 255, 255)' + let textTop = top + 12 + context.fillText(featureType, startPx + 2, textTop) + if (featureName) { + textTop = textTop + 12 + context.fillText(`Name: ${featureName}`, startPx + 2, textTop) + } + textTop = textTop + 12 + context.fillText(featureStart, startPx + 2, textTop) + textTop = textTop + 12 + context.fillText(featureEnd, startPx + 2, textTop) + } + getContextMenuItems(display: LinearApolloDisplayMouseEvents): MenuItem[] { const { apolloHover, diff --git a/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/ImplicitExonGeneGlyph.ts b/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/ImplicitExonGeneGlyph.ts index 92db1fbae..7a3e0ec11 100644 --- a/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/ImplicitExonGeneGlyph.ts +++ b/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/glyphs/ImplicitExonGeneGlyph.ts @@ -1,7 +1,6 @@ import { AnnotationFeatureI } from 'apollo-mst' import { LinearApolloDisplay } from '../stateModel' -import { LinearApolloDisplayMouseEvents } from '../stateModel/mouseEvents' import { CanvasMouseEvent } from '../types' import { Glyph } from './Glyph' @@ -63,73 +62,6 @@ export class ImplicitExonGeneGlyph extends Glyph { return mrnaCount } - drawTooltip( - linearApolloDisplayMouseEvents: LinearApolloDisplayMouseEvents, - context: CanvasRenderingContext2D, - ) { - const { apolloHover, lgv, apolloRowHeight, displayedRegions } = - linearApolloDisplayMouseEvents - if (!apolloHover) { - return - } - const { feature, mousePosition } = apolloHover - if (!(feature && mousePosition)) { - return - } - const { regionNumber, y } = mousePosition - const displayedRegion = displayedRegions[regionNumber] - const { refName, reversed } = displayedRegion - const { bpPerPx, bpToPx, offsetPx } = lgv - const { start, end, length } = feature - let startPx = - (bpToPx({ refName, coord: reversed ? end : start, regionNumber }) - ?.offsetPx ?? 0) - offsetPx - const row = Math.floor(y / apolloRowHeight) - const top = row * apolloRowHeight - const widthPx = length / bpPerPx - - const featureType = `Type: ${feature.type}` - const featureName = feature.attributes - .get('gff_name') - ?.find((name) => name !== '') - const featureStart = `Start: ${feature.start.toString()}` - const featureEnd = `End: ${feature.end.toString()}` - const textWidth = [ - context.measureText(featureType).width, - context.measureText(featureStart).width, - context.measureText(featureEnd).width, - ] - if (featureName) { - textWidth.push(context.measureText(`Name: ${featureName}`).width) - } - const maxWidth = Math.max(...textWidth) - - startPx = startPx + widthPx + 5 - context.fillStyle = 'rgba(1, 1, 1, 0.7)' - context.fillRect( - startPx, - top, - maxWidth + 4, - textWidth.length === 4 ? 55 : 45, - ) - context.beginPath() - context.moveTo(startPx, top) - context.lineTo(startPx - 5, top + 5) - context.lineTo(startPx, top + 10) - context.fill() - context.fillStyle = 'rgba(255, 255, 255)' - let textTop = top + 12 - context.fillText(featureType, startPx + 2, textTop) - if (featureName) { - textTop = textTop + 12 - context.fillText(`Name: ${featureName}`, startPx + 2, textTop) - } - textTop = textTop + 12 - context.fillText(featureStart, startPx + 2, textTop) - textTop = textTop + 12 - context.fillText(featureEnd, startPx + 2, textTop) - } - draw( stateModel: LinearApolloDisplay, ctx: CanvasRenderingContext2D, diff --git a/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/stateModel/rendering.ts b/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/stateModel/rendering.ts index d1b8d0422..a8397ef6b 100644 --- a/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/stateModel/rendering.ts +++ b/packages/jbrowse-plugin-apollo/src/LinearApolloDisplay/stateModel/rendering.ts @@ -23,6 +23,7 @@ export function renderingModelIntermediateFactory( apolloRowHeight: 20, detailsMinHeight: 200, detailsHeight: 200, + lastRowTooltipBufferHeight: 40, isShown: true, }) .volatile(() => ({ @@ -33,7 +34,10 @@ export function renderingModelIntermediateFactory( })) .views((self) => ({ get featuresHeight() { - return (self.highestRow + 1) * self.apolloRowHeight + return ( + (self.highestRow + 1) * self.apolloRowHeight + + self.lastRowTooltipBufferHeight + ) }, })) .actions((self) => ({