diff --git a/python/neuroglancer/viewer_config_state.py b/python/neuroglancer/viewer_config_state.py index 016a229ef..f16af1290 100644 --- a/python/neuroglancer/viewer_config_state.py +++ b/python/neuroglancer/viewer_config_state.py @@ -96,6 +96,31 @@ class LayerSelectionState(JsonObjectWrapper): class LayerSelectedValues(_LayerSelectedValuesBase): """Specifies the data values associated with the current mouse position.""" +@export +class PanelResolutionData(JsonObjectWrapper): + __slots__ = () + type = wrapped_property("type", str) + width = wrapped_property("width", int) + height = wrapped_property("height", int) + resolution = wrapped_property("resolution", str) + +@export +class LayerResolutionData(JsonObjectWrapper): + __slots__ = () + name = wrapped_property("name", str) + type = wrapped_property("type", str) + resolution = wrapped_property("resolution", str) + +@export +class ScreenshotResolutionMetadata(JsonObjectWrapper): + __slots__ = () + panel_resolution_data = panelResolutionData = wrapped_property( + "panelResolutionData", typed_list(PanelResolutionData) + ) + layer_resolution_data = layerResolutionData = wrapped_property( + "layerResolutionData", typed_list(LayerResolutionData) + ) + @export class ScreenshotReply(JsonObjectWrapper): @@ -107,7 +132,7 @@ class ScreenshotReply(JsonObjectWrapper): image_type = imageType = wrapped_property("imageType", str) depth_data = depthData = wrapped_property("depthData", optional(base64.b64decode)) resolution_metadata = resolutionMetadata = wrapped_property( - "resolutionMetadata", dict + "resolutionMetadata", ScreenshotResolutionMetadata ) @property diff --git a/src/ui/screenshot_menu.css b/src/ui/screenshot_menu.css index 00455b90c..7ec2c1450 100644 --- a/src/ui/screenshot_menu.css +++ b/src/ui/screenshot_menu.css @@ -14,53 +14,26 @@ * limitations under the License. */ -:root { - --blue: #1e90ff; - --white: #ffffff; +/* Color variables for the screenshot */ +.neuroglancer-screenshot-overlay { --gray300: #d0d5dd; --gray600: #141415; --gray500: #f7f7f7; - --gray100: #ebebeb; --gray50: #e6e6e6; --gray800: #344054; - --primary500: #0069eb; - --primary700: #0474ff; --gray700: rgba(20, 20, 21, 0.6); --gray400: rgba(20, 20, 21, 0.4); --gray200: rgba(20, 20, 21, 0.8); + --primary500: #0069eb; + --primary700: #0474ff; } -.neuroglancer-screenshot-overlay { - overflow-x: hidden; - overflow-y: auto; -} -.neuroglancer-screenshot-overlay - :is( - div, - span, - a, - img, - h1, - h2, - h3, - h4, - h5, - h6, - p, - form, - input, - textarea, - select, - strong, - table, - tr, - td, - th, - tbody, - button - ) { + +/* General headings, labels, and top-level containers */ +.neuroglancer-screenshot-overlay :is(div, table, tr, td, th, ) { box-sizing: border-box; outline: 0; } + .neuroglancer-screenshot-dialog { width: 48.75rem; padding: 0; @@ -72,97 +45,54 @@ border-radius: 0.5rem; font-family: sans-serif; } -.neuroglancer-screenshot-dialog .neuroglancer-screenshot-close-and-help { - border-bottom: 1px solid var(--gray50); - display: flex; - color: var(--gray600); - align-items: center; - display: flex; - padding: 0.75rem 1rem; - align-items: center; - gap: 10px; -} .neuroglancer-screenshot-main-body-container { - height: auto !important; - max-height: calc(100vh - 200px) !important; + height: auto; + max-height: calc(100vh - 200px); overflow-y: auto; overflow-x: hidden; } -.neuroglancer-screenshot-dialog .neuroglancer-screenshot-title-heading { +.neuroglancer-screenshot-title { font-size: 0.938rem; font-weight: 590; + color: var(--gray600); margin: 0; } -.neuroglancer-screenshot-scale-menu { - padding: 0 1rem; - display: flex; - flex-wrap: wrap; -} -.neuroglancer-screenshot-scale-menu label { - color: var(--gray700); - font-size: 0.75rem; - margin-right: 2.125rem; -} -.neuroglancer-screenshot-scale-menu .neuroglancer-screenshot-scale-radio { - margin: 0 0.188rem 0 0; -} -.neuroglancer-screenshot-keep-slice-fov-checkbox { - margin: 0; +.neuroglancer-screenshot-title-subheading { + display: flex; + align-items: center; + justify-content: space-between; } -.neuroglancer-screenshot-size-text, -.neuroglancer-screenshot-keep-slice-label, -.neuroglancer-screenshot-scale-menu .neuroglancer-screenshot-scale-factor { +.neuroglancer-screenshot-label { font-size: 0.813rem; color: var(--gray200); font-weight: 590; } -.neuroglancer-screenshot-scale-factor { - width: 100%; - margin-bottom: 0.5rem; - display: flex; - align-items: center; - gap: 0.15rem; -} -.neuroglancer-screenshot-keep-slice-label { - display: flex; - flex-direction: row-reverse; - margin: 1rem 0; - width: 100%; - justify-content: flex-end; - align-items: center; - gap: 0.5rem; -} -.neuroglancer-screenshot-filename-and-buttons - .neuroglancer-screenshot-title-subheading { - font-size: 0.938rem; - font-weight: 590; - margin: 0; + +/* Div at the top which contains the close */ +.neuroglancer-screenshot-close { + border-bottom: 1px solid var(--gray50); display: flex; align-items: center; - color: var(--gray600); - justify-content: space-between; -} - -.neuroglancer-screenshot-scale-radio { - display: inline-block; - cursor: pointer; + padding: 0.75rem 1rem; + gap: 10px; } -.neuroglancer-screenshot-filename-and-buttons { +/* Filename input menu */ +.neuroglancer-screenshot-filename-container { padding: 1rem 1rem 0.75rem 1rem; } -.neuroglancer-screenshot-filename-and-buttons label { + +.neuroglancer-screenshot-name-label { color: var(--gray800); - font-size: 0.813rem; font-style: normal; - font-weight: 590; display: block; margin: 0.75rem 0 0.375rem; } + .neuroglancer-screenshot-name-input { width: 100%; margin-right: 10px; @@ -178,15 +108,77 @@ color: var(--gray700); box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05); line-height: 20px; + box-sizing: border-box; + outline: 0; } + .neuroglancer-screenshot-name-input:disabled { background: var(--gray500); color: var(--gray400); } + .neuroglancer-screenshot-name-input::placeholder { color: var(--gray700); } +/* Scale selection menu */ +.neuroglancer-screenshot-scale-factor-label { + width: 100%; + margin-bottom: 0.5rem; + display: flex; + align-items: center; + gap: 0.15rem; +} + +.neuroglancer-screenshot-scale-menu { + padding: 0 1rem; + display: flex; + flex-wrap: wrap; +} + +.neuroglancer-screenshot-scale-radio-container { + display: flex; + flex-direction: row; +} + +.neuroglancer-screenshot-scale-radio-item { + margin-right: 2.125rem; +} + +.neuroglancer-screenshot-scale-radio-label { + color: var(--gray700); + font-size: 0.75rem; +} + +.neuroglancer-screenshot-scale-radio-input { + margin: 0 0.188rem 0 0; + display: inline-block; + cursor: pointer; +} + +.neuroglancer-screenshot-warning { + color: red; + width: auto; + font-size: 0.75rem; + margin: 0.12rem 0 0 -1.25rem; +} + +/* Slice FOV fixed selection menu */ +.neuroglancer-screenshot-keep-slice-fov-checkbox { + margin: 0; +} + +.neuroglancer-screenshot-keep-slice-label { + display: flex; + flex-direction: row-reverse; + margin: 1rem 0; + width: 100%; + justify-content: flex-end; + align-items: center; + gap: 0.5rem; +} + +/* Take screenshot and close buttons */ .neuroglancer-screenshot-button { cursor: pointer; margin: 2px 0px; @@ -199,126 +191,76 @@ border: 0; } -.neuroglancer-screenshot-statistics-title { - padding: 1rem; -} - -.neuroglancer-screenshot-statistics-table { - width: 100%; - border-collapse: collapse; +/* Screenshot resolution table - voxel resolution, panel resolution */ +.neuroglancer-screenshot-size-text { + margin: 0.75rem 0 0.75rem 0; + display: flex; + align-items: center; } -.neuroglancer-screenshot-statistics-table th, -.neuroglancer-screenshot-statistics-table td { +.neuroglancer-screenshot-resolution-size-label { text-align: left; - width: 33.33%; -} -.neuroglancer-screenshot-statistics-table td { color: var(--gray200); font-size: 0.813rem; + font-style: normal; font-weight: 590; - padding: 0.5rem 0; + width: 33.33%; + padding: 0; + margin: 0 0; } -.neuroglancer-screenshot-statistics-table th[colspan="2"] { + +.neuroglancer-screenshot-resolution-size-value { font-size: 0.813rem; color: var(--gray700); - line-height: 1.25rem; + width: 33.33%; + padding: 0.25rem; font-weight: 400; - padding-bottom: 0.75rem; } -.neuroglancer-screenshot-statistics-table th[colspan="2"] a { - font-weight: 590; - cursor: pointer; - color: var(--primary500); - margin-left: 0.1rem; -} -.neuroglancer-screenshot-statistics-table td:last-child { - color: var(--gray700); - font-weight: 400; -} -.neuroglancer-screenshot-statistics-table th { - font-size: 0.938rem; - font-weight: 590; - margin: 0; - color: var(--gray600); - padding: 0 0 0.5rem; -} .neuroglancer-screenshot-resolution-preview-container { border-top: 1px solid var(--gray50); border-bottom: 1px solid var(--gray50); background: var(--gray500); width: 100%; - padding: 1rem; -} -.neuroglancer-screenshot-resolution-preview-container h2 { - font-size: 0.938rem; - font-weight: 590; - margin: 0; - color: var(--gray600); + padding: 1rem 1rem 0.5rem 1rem; } -.neuroglancer-screenshot-resolution-preview-top-container { - display: flex; -} .neuroglancer-screenshot-resolution-table { width: 100%; border-collapse: separate; border-spacing: 0; -} - -.neuroglancer-screenshot-resolution-table table { padding: 4px 2px 2px 2px; - background-color: #e6e6e6; - border-radius: 6px; + background-color: var(--gray50); margin-bottom: 0.75rem; + border-radius: 0.35rem; + border: 0; } -.neuroglancer-screenshot-resolution-table table tr:nth-child(2) td:first-child { - border-top-left-radius: 0.25rem; -} - -.neuroglancer-screenshot-resolution-table table tr:nth-child(2) td:last-child { - border-top-right-radius: 0.25rem; -} - -.neuroglancer-screenshot-resolution-table table tr:last-child td:first-child { - border-bottom-left-radius: 0.25rem; -} - -.neuroglancer-screenshot-resolution-table table tr:last-child td:last-child { - border-bottom-right-radius: 0.25rem; -} - -.neuroglancer-screenshot-resolution-table h3 { +.neuroglancer-screenshot-resolution-table th { + font-size: 0.813rem; + width: 33.33%; text-align: left; color: var(--gray200); - font-size: 0.813rem; font-style: normal; font-weight: 590; - width: 33.33%; - padding: 0; - margin: 0 0; -} -.neuroglancer-screenshot-size-text { - margin: 0.75rem 0 0.75rem 0; - display: flex; - align-items: center; + background: var(--gray50); + padding: 0.25rem 0.375rem; } -.neuroglancer-screenshot-size-text h3 { - margin: 0; - width: 33.33%; +.neuroglancer-screenshot-resolution-table td { font-size: 0.813rem; + width: 33.33%; + color: var(--gray700); + background: white; + padding: 0.375rem; + line-height: 1.25rem; } -.neuroglancer-screenshot-size-text span { - font-size: 0.813rem; - color: var(--gray700); - width: 33.33%; - padding: 0.25rem; - font-weight: 400; +.neuroglancer-screenshot-resolution-table-tooltip { + vertical-align: top; + margin-left: 0.25rem; } + .neuroglancer-screenshot-copy-icon { outline: 0; border: 0; @@ -326,184 +268,85 @@ height: 1rem; margin-left: auto; position: relative; - width: 33.33% !important; + width: 33.33%; justify-content: flex-end; } -.neuroglancer-screenshot-dialog .neuroglancer-icon svg { - stroke: rgba(20, 20, 21, 0.4); - width: 1rem; - height: 1rem; -} -.neuroglancer-screenshot-dialog .neuroglancer-icon { - width: 1rem; - height: 1rem; - min-width: inherit; - min-height: inherit; - padding: 0; -} -.neuroglancer-screenshot-dialog .neuroglancer-icon:hover { - background: none; + +.neuroglancer-screenshot-dimension { + color: var(--gray600); } -.neuroglancer-screenshot-size-text + .neuroglancer-screenshot-resolution-table { - border: 0; + +/* Screenshot statistics table - shows GPU memory etc. */ +.neuroglancer-screenshot-statistics-table { + width: 100%; + border-collapse: collapse; } -.neuroglancer-screenshot-resolution-table - + .neuroglancer-screenshot-resolution-table { - border: 0; + +.neuroglancer-screenshot-statistics-table th { + text-align: left; + width: 33.33%; + padding: 0 0 0.5rem; } -.neuroglancer-screenshot-resolution-table th { + +.neuroglancer-screenshot-statistics-table td { text-align: left; - color: var(--gray200); - font-size: 0.813rem; - font-style: normal; - font-weight: 590; width: 33.33%; - background: #e6e6e6; - padding: 0.25rem 0.375rem; + font-size: 0.813rem; + padding: 0.5rem 0; } -.neuroglancer-screenshot-resolution-table th .neuroglancer-screenshot-tooltip, -.neuroglancer-screenshot-scale-factor .neuroglancer-screenshot-tooltip { - flex: none; - cursor: pointer; - width: 1rem; - height: 1rem; - background-size: contain; - position: relative; - display: inline-block; - margin-left: 0.25rem; - position: relative; - vertical-align: top; +.neuroglancer-screenshot-statistics-table-data-key { + color: var(--gray200); + font-weight: 590; } -.neuroglancer-screenshot-resolution-table - th - .neuroglancer-screenshot-tooltip:hover::after, -.neuroglancer-screenshot-resolution-table - th - .neuroglancer-screenshot-tooltip:hover::before, -.neuroglancer-screenshot-scale-factor - .neuroglancer-screenshot-tooltip:hover::after, -.neuroglancer-screenshot-scale-factor - .neuroglancer-screenshot-tooltip:hover::before { - opacity: 1; - visibility: visible; -} - -.neuroglancer-screenshot-resolution-table - th - .neuroglancer-screenshot-tooltip::after, -.neuroglancer-screenshot-scale-factor .neuroglancer-screenshot-tooltip::after { - content: attr(data-tooltip); - position: absolute; - border-width: 0.375rem; - border-style: solid; - opacity: 0; - visibility: hidden; - transition: opacity 0.3s ease; - pointer-events: none; - border-color: transparent transparent transparent var(--gray600); - left: 160%; -} - -.neuroglancer-screenshot-resolution-table - th - .neuroglancer-screenshot-tooltip::before, -.neuroglancer-screenshot-scale-factor .neuroglancer-screenshot-tooltip::before { - content: ""; - position: absolute; - top: 0; /* Adjust this to move the arrow closer to the tooltip */ - left: 100%; - border-width: 0.375rem; - border-style: solid; - border-color: transparent var(--gray600) transparent transparent; /* Arrow pointing leftwards */ - opacity: 0; - visibility: hidden; - transition: opacity 0.3s ease; - pointer-events: none; +.neuroglancer-screenshot-statistics-table-data-value { + color: var(--gray700); + font-weight: 400; } -.neuroglancer-screenshot-resolution-table td { +.neuroglancer-statistics-table-description-header { font-size: 0.813rem; color: var(--gray700); - width: 33.33%; - background: white; - padding: 0.375rem; line-height: 1.25rem; + font-weight: 400; } -.neuroglancer-screenshot-warning { - color: red; - width: auto; - font-size: 0.75rem; - margin: 0.12rem 0 0 -1.25rem; +.neuroglancer-statistics-table-description-link { + font-weight: 590; + cursor: pointer; + color: var(--primary500); + margin-left: 0.1rem; } -.neuroglancer-screenshot-tooltip { - flex: none; - cursor: pointer; +/* Icons in the dialog */ +.neuroglancer-screenshot-dialog .neuroglancer-icon svg { + stroke: rgba(20, 20, 21, 0.4); width: 1rem; height: 1rem; - background-size: contain; - position: relative; - cursor: pointer; } -.neuroglancer-screenshot-tooltip::after { - content: attr(data-tooltip); - position: absolute; - text-indent: 0; - color: white; - white-space: normal; - padding: 0.5rem 0.75rem; - border-radius: 5px; - height: auto; - width: 20rem; - line-height: 1rem; - font-size: 0.75rem; - font-weight: 590; - border-radius: 8px; - background: var(--gray600); - text-align: center; - opacity: 0; - visibility: hidden; - z-index: 99999; - transition: opacity 0.3s ease; - pointer-events: none; - right: 150%; - top: -100%; -} -.neuroglancer-screenshot-tooltip::before { - content: ""; - position: absolute; - border-width: 0.375rem; - border-style: solid; - opacity: 0; - visibility: hidden; - transition: opacity 0.3s ease; - pointer-events: none; - border-color: transparent transparent transparent black; - top: 0; - left: -50%; -} - -.neuroglancer-screenshot-tooltip:hover::after, -.neuroglancer-screenshot-tooltip:hover::before { - opacity: 1; - visibility: visible; -} -.neuroglancer-screenshot-close-and-help .neuroglancer-screenshot-tooltip { - position: absolute; - right: 0.875rem; - top: 4.4rem; + +.neuroglancer-screenshot-dialog .neuroglancer-icon { + width: 1rem; + height: 1rem; + min-width: inherit; + min-height: inherit; + padding: 0; } + +.neuroglancer-screenshot-dialog .neuroglancer-icon:hover { + background: none; +} + +/* Footer with progress and buttons */ .neuroglancer-screenshot-footer-container { margin: 0; display: flex; padding: 0.75rem 1rem; border-top: 1px solid var(--gray50); } -.neuroglancer-screenshot-footer-container - .neuroglancer-screenshot-progress-text { + +.neuroglancer-screenshot-progress-text { margin: 0; flex: 1; font-weight: 590; @@ -513,7 +356,8 @@ align-self: center; color: var(--primary700); } -.neuroglancer-screenshot-footer-container .neuroglancer-screenshot-button { + +.neuroglancer-screenshot-footer-button { border-radius: 0.5rem; border: 1px solid var(--gray300); background: white; @@ -523,54 +367,7 @@ color: var(--gray800); margin: 0 0 0 0.25rem; } -.neuroglancer-screenshot-footer-container - .neuroglancer-screenshot-button:disabled { - display: none; -} -.neuroglancer-screenshot-copy-icon::after { - content: attr(data-tooltip); - position: absolute; - text-indent: 0; - color: white; - padding: 0.5rem 0.75rem; - border-radius: 5px; - height: auto; - width: 8.125rem; - line-height: 1rem; - font-size: 0.75rem; - font-weight: 590; - border-radius: 8px; - background: var(--gray600); - text-align: center; - opacity: 0; - visibility: hidden; - z-index: 9999; - transition: opacity 0.3s ease; - pointer-events: none; /* Prevent the tooltip from blocking interaction */ - right: 10%; - top: -50%; -} -.neuroglancer-screenshot-copy-icon::before { - content: ""; - position: absolute; - border-width: 0.375rem; - border-style: solid; - opacity: 0; - visibility: hidden; - transition: opacity 0.3s ease; - pointer-events: none; - border-color: transparent transparent transparent black; - top: 0; - left: 90%; -} - -.neuroglancer-screenshot-copy-icon:hover::after, -.neuroglancer-screenshot-copy-icon:hover::before { - opacity: 1; - visibility: visible; -} - -.neuroglancer-screenshot-dimension { - color: var(--gray600); +.neuroglancer-screenshot-footer-button:disabled { + display: none; } diff --git a/src/ui/screenshot_menu.ts b/src/ui/screenshot_menu.ts index 6cbbcf433..dde37b114 100644 --- a/src/ui/screenshot_menu.ts +++ b/src/ui/screenshot_menu.ts @@ -88,6 +88,24 @@ const layerNamesForUI = { SegmentationRenderLayer: "Segmentation slice (2D)", }; +function splitIntoLines(text: string, maxLineLength: number = 60): string { + const words = text.split(" "); + const lines = []; + let currentLine = ""; + + for (const word of words) { + if ((currentLine + word).length > maxLineLength) { + lines.push(currentLine.trim()); + currentLine = word + " "; + } else { + currentLine += word + " "; + } + } + lines.push(currentLine.trim()); + + return lines.join("\n"); +} + /** * Combine the resolution of all dimensions into a single string for UI display */ @@ -160,7 +178,7 @@ export class ScreenshotDialog extends Overlay { private panelResolutionTable: HTMLTableElement; private layerResolutionTable: HTMLTableElement; private statisticsContainer: HTMLDivElement; - private filenameAndButtonsContainer: HTMLDivElement; + private filenameInputContainer: HTMLDivElement; private screenshotSizeText: HTMLDivElement; private warningElement: HTMLDivElement; private footerScreenshotActionBtnsContainer: HTMLDivElement; @@ -219,33 +237,32 @@ export class ScreenshotDialog extends Overlay { } private setupHelpTooltips() { - const generalSettingsTooltip = makeIcon({ svg: svg_help }); - generalSettingsTooltip.classList.add("neuroglancer-screenshot-tooltip"); - generalSettingsTooltip.setAttribute( - "data-tooltip", - TOOLTIPS.generalSettingsTooltip, - ); + const generalSettingsTooltip = makeIcon({ + svg: svg_help, + title: splitIntoLines(TOOLTIPS.generalSettingsTooltip), + }); - const orthographicSettingsTooltip = makeIcon({ svg: svg_help }); + const orthographicSettingsTooltip = makeIcon({ + svg: svg_help, + title: TOOLTIPS.orthographicSettingsTooltip, + }); orthographicSettingsTooltip.classList.add( - "neuroglancer-screenshot-tooltip", + "neuroglancer-screenshot-resolution-table-tooltip", ); - orthographicSettingsTooltip.setAttribute( - "data-tooltip", - TOOLTIPS.orthographicSettingsTooltip, - ); - - const layerDataTooltip = makeIcon({ svg: svg_help }); - layerDataTooltip.classList.add("neuroglancer-screenshot-tooltip"); - layerDataTooltip.setAttribute("data-tooltip", TOOLTIPS.layerDataTooltip); - const scaleFactorHelpTooltip = makeIcon({ svg: svg_help }); - scaleFactorHelpTooltip.classList.add("neuroglancer-screenshot-tooltip"); - scaleFactorHelpTooltip.setAttribute( - "data-tooltip", - TOOLTIPS.scaleFactorHelpTooltip, + const layerDataTooltip = makeIcon({ + svg: svg_help, + title: splitIntoLines(TOOLTIPS.layerDataTooltip), + }); + layerDataTooltip.classList.add( + "neuroglancer-screenshot-resolution-table-tooltip", ); + const scaleFactorHelpTooltip = makeIcon({ + svg: svg_help, + title: splitIntoLines(TOOLTIPS.scaleFactorHelpTooltip), + }); + return (this.helpTooltips = { generalSettingsTooltip, orthographicSettingsTooltip, @@ -263,7 +280,7 @@ export class ScreenshotDialog extends Overlay { } const titleText = document.createElement("h2"); - titleText.classList.add("neuroglancer-screenshot-title-heading"); + titleText.classList.add("neuroglancer-screenshot-title"); titleText.textContent = "Screenshot"; this.closeMenuButton = this.createButton( @@ -273,34 +290,41 @@ export class ScreenshotDialog extends Overlay { svg_close, ); - this.cancelScreenshotButton = this.createButton("Cancel screenshot", () => - this.cancelScreenshot(), + this.cancelScreenshotButton = this.createButton( + "Cancel screenshot", + () => this.cancelScreenshot(), + "neuroglancer-screenshot-footer-button", ); - this.takeScreenshotButton = this.createButton("Take screenshot", () => - this.screenshot(), + this.takeScreenshotButton = this.createButton( + "Take screenshot", + () => this.screenshot(), + "neuroglancer-screenshot-footer-button", ); - this.forceScreenshotButton = this.createButton("Force screenshot", () => - this.forceScreenshot(), + this.forceScreenshotButton = this.createButton( + "Force screenshot", + () => this.forceScreenshot(), + "neuroglancer-screenshot-footer-button", ); - this.filenameAndButtonsContainer = document.createElement("div"); - this.filenameAndButtonsContainer.classList.add( - "neuroglancer-screenshot-filename-and-buttons", + this.filenameInputContainer = document.createElement("div"); + this.filenameInputContainer.classList.add( + "neuroglancer-screenshot-filename-container", ); const menuText = document.createElement("h3"); menuText.classList.add("neuroglancer-screenshot-title-subheading"); + menuText.classList.add("neuroglancer-screenshot-title"); menuText.textContent = "Settings"; menuText.appendChild(tooltips.generalSettingsTooltip); - this.filenameAndButtonsContainer.appendChild(menuText); + this.filenameInputContainer.appendChild(menuText); const nameInputLabel = document.createElement("label"); nameInputLabel.textContent = "Screenshot name"; - this.filenameAndButtonsContainer.appendChild(nameInputLabel); - this.filenameAndButtonsContainer.appendChild(this.createNameInput()); + nameInputLabel.classList.add("neuroglancer-screenshot-label"); + nameInputLabel.classList.add("neuroglancer-screenshot-name-label"); + this.filenameInputContainer.appendChild(nameInputLabel); + this.filenameInputContainer.appendChild(this.createNameInput()); const closeAndHelpContainer = document.createElement("div"); - closeAndHelpContainer.classList.add( - "neuroglancer-screenshot-close-and-help", - ); + closeAndHelpContainer.classList.add("neuroglancer-screenshot-close"); closeAndHelpContainer.appendChild(titleText); closeAndHelpContainer.appendChild(this.closeMenuButton); @@ -312,7 +336,7 @@ export class ScreenshotDialog extends Overlay { mainBody.classList.add("neuroglancer-screenshot-main-body-container"); this.content.appendChild(mainBody); - mainBody.appendChild(this.filenameAndButtonsContainer); + mainBody.appendChild(this.filenameInputContainer); mainBody.appendChild(this.createScaleRadioButtons()); const previewContainer = document.createElement("div"); @@ -320,21 +344,33 @@ export class ScreenshotDialog extends Overlay { "neuroglancer-screenshot-resolution-preview-container", ); const settingsPreview = document.createElement("div"); - settingsPreview.classList.add("neuroglancer-screenshot-resolution-table"); + settingsPreview.classList.add( + "neuroglancer-screenshot-resolution-table-container", + ); const previewTopContainer = document.createElement("div"); previewTopContainer.classList.add( "neuroglancer-screenshot-resolution-preview-top-container", ); + previewTopContainer.style.display = "flex"; const previewLabel = document.createElement("h2"); + previewLabel.classList.add("neuroglancer-screenshot-title"); previewLabel.textContent = "Preview"; this.screenshotSizeText = document.createElement("div"); + this.screenshotSizeText.classList.add("neuroglancer-screenshot-label"); this.screenshotSizeText.classList.add("neuroglancer-screenshot-size-text"); const screenshotLabel = document.createElement("h3"); screenshotLabel.textContent = "Screenshot size"; + screenshotLabel.classList.add( + "neuroglancer-screenshot-resolution-size-label", + ); this.screenshotPixelSize = document.createElement("span"); + this.screenshotPixelSize.classList.add( + "neuroglancer-screenshot-resolution-size-value", + ); const screenshotCopyButton = makeCopyButton({ + title: "Copy table to clipboard", onClick: () => { const result = setClipboard(this.getResolutionText()); StatusMessage.showTemporaryMessage( @@ -345,10 +381,6 @@ export class ScreenshotDialog extends Overlay { }, }); screenshotCopyButton.classList.add("neuroglancer-screenshot-copy-icon"); - screenshotCopyButton.setAttribute( - "data-tooltip", - "Copy table to clipboard", - ); this.screenshotSizeText.appendChild(screenshotLabel); this.screenshotSizeText.appendChild(this.screenshotPixelSize); @@ -443,7 +475,8 @@ export class ScreenshotDialog extends Overlay { scaleMenu.classList.add("neuroglancer-screenshot-scale-menu"); const scaleLabel = document.createElement("label"); - scaleLabel.classList.add("neuroglancer-screenshot-scale-factor"); + scaleLabel.classList.add("neuroglancer-screenshot-scale-factor-label"); + scaleLabel.classList.add("neuroglancer-screenshot-label"); scaleLabel.textContent = "Screenshot scale factor"; scaleLabel.appendChild(this.helpTooltips.scaleFactorHelpTooltip); @@ -462,6 +495,7 @@ export class ScreenshotDialog extends Overlay { const scales = [1, 2, 4]; scales.forEach((scale) => { + const container = document.createElement("div"); const label = document.createElement("label"); const input = document.createElement("input"); @@ -469,11 +503,15 @@ export class ScreenshotDialog extends Overlay { input.name = "screenshot-scale"; input.value = scale.toString(); input.checked = scale === this.screenshotManager.screenshotScale; - input.classList.add("neuroglancer-screenshot-scale-radio"); + input.classList.add("neuroglancer-screenshot-scale-radio-input"); - label.appendChild(input); label.appendChild(document.createTextNode(`${scale}x`)); - this.scaleRadioButtonsContainer.appendChild(label); + label.classList.add("neuroglancer-screenshot-scale-radio-label"); + + container.classList.add("neuroglancer-screenshot-scale-radio-item"); + container.appendChild(input); + container.appendChild(label); + this.scaleRadioButtonsContainer.appendChild(container); input.addEventListener("change", () => { this.screenshotManager.screenshotScale = scale; @@ -486,6 +524,7 @@ export class ScreenshotDialog extends Overlay { keepSliceFOVFixedDiv.classList.add( "neuroglancer-screenshot-keep-slice-label", ); + keepSliceFOVFixedDiv.classList.add("neuroglancer-screenshot-label"); keepSliceFOVFixedDiv.textContent = "Keep slice FOV fixed with scale change"; const keepSliceFOVFixedCheckbox = document.createElement("input"); @@ -512,6 +551,7 @@ export class ScreenshotDialog extends Overlay { this.statisticsContainer.classList.add( "neuroglancer-screenshot-statistics-title", ); + this.statisticsContainer.style.padding = "1rem"; this.statisticsTable = document.createElement("table"); this.statisticsTable.classList.add( @@ -521,6 +561,7 @@ export class ScreenshotDialog extends Overlay { const headerRow = this.statisticsTable.createTHead().insertRow(); const keyHeader = document.createElement("th"); keyHeader.textContent = "Screenshot progress"; + keyHeader.classList.add("neuroglancer-screenshot-title"); headerRow.appendChild(keyHeader); const valueHeader = document.createElement("th"); valueHeader.textContent = ""; @@ -528,6 +569,9 @@ export class ScreenshotDialog extends Overlay { const descriptionRow = this.statisticsTable.createTHead().insertRow(); const descriptionkeyHeader = document.createElement("th"); + descriptionkeyHeader.classList.add( + "neuroglancer-statistics-table-description-header", + ); descriptionkeyHeader.colSpan = 2; descriptionkeyHeader.textContent = @@ -536,6 +580,7 @@ export class ScreenshotDialog extends Overlay { // It can be used to point to a docs page when complete // const descriptionLearnMoreLink = document.createElement("a"); // descriptionLearnMoreLink.text = "Learn more"; + // descriptionLearnMoreLink.classList.add("neuroglancer-statistics-table-description-link") // descriptionkeyHeader.appendChild(descriptionLearnMoreLink); descriptionRow.appendChild(descriptionkeyHeader); @@ -549,7 +594,13 @@ export class ScreenshotDialog extends Overlay { for (const key in orderedStatsRow) { const row = this.statisticsTable.insertRow(); const keyCell = row.insertCell(); + keyCell.classList.add( + "neuroglancer-screenshot-statistics-table-data-key", + ); const valueCell = row.insertCell(); + valueCell.classList.add( + "neuroglancer-screenshot-statistics-table-data-value", + ); keyCell.textContent = statisticsNamesForUI[key as keyof typeof statisticsNamesForUI]; valueCell.textContent =