/*\ |*| Note: layout.rtl.css is a generated file. Only edit layout.css. |*| layout.rtl.css is generated automatically while running the dev server (npm run dev) |*| (or manually with npm run build-css) |*| |*| Right-to-left layout is handled with a processor called RTLCSS, using comment directives. |*| Note in particular that "direction: ltr;" by default gets flipped to "direction: rtl;", |*| so the way to make a piece of UI left-to-right-only is with an ignore directive. \*/ html, body, .jspaint { width: 100%; height: 100%; margin: 0; padding: 0; border: 0; overflow: hidden; } .jspaint { direction: ltr; box-sizing: border-box; /* for iPad fullscreen workaround, which adds padding to .jspaint; without this it hides the status bar */ /* prevent selection, especially for Safari on iOS/iPad, which likes to select random (nearest, kind of?) elements when long-pressing */ -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .chooser-option { display: flex; } .choose-shape-style { display: flex; flex-flow: column; } .choose-eraser, .choose-magnification, .choose-stroke-size, .choose-transparent-mode { display: flex; flex-flow: column; align-items: center; justify-content: space-around; } .choose-brush, .choose-airbrush-size { display: flex; flex-flow: row wrap; justify-content: space-around; align-content: space-around; } .tool-options canvas { flex: 0 0 auto; } .component-window .window-content, .component-window .window-content :not(table):not(tbody):not(tr):not(td) { display: flex; } .jspaint { display: flex; flex-flow: column; flex: 1; } .horizontal { display: flex; flex-flow: row; flex: 1 1 0; overflow: hidden; } .vertical { display: flex; flex-flow: column; flex: 1; } .jspaint > .vertical { height: 100%; } @media (max-width: 200px) { .horizontal > .component-area { display: none; } } @media (max-height: 340px) { .vertical > .component-area { display: none; } } @media (max-height: 359px) { .vertical > .status-area { display: none !important; } } .window.squish, .window.squish .window-content { max-width: 100vw; max-height: 100vh; } .window:not(.squish) { white-space: nowrap; } .component-area { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .tool-window .window-titlebar { direction: ltr; text-align: start; } .status-area, .component-area { display: flex; flex: 0 0 auto; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .selection, .textbox, .helper-layer { z-index: 3; } .selection, .textbox { display: block !important; /* @TODO: reduce overzealous display: flex; */ box-sizing: border-box; -moz-box-sizing: border-box; } .textbox > img, .textbox > canvas, .selection > img, .selection > canvas { /* @TODO: maybe don't include the canvas in the DOM (is it helpful to inspect it tho? it's not critical...) */ opacity: 0; /* Fix draggable part of selection going outside the selection selection is vertically thin */ position: absolute; left: 0; top: 0; } .selection > img, .selection > canvas, .helper-layer > canvas { width: 100%; height: 100%; } .helper-layer > canvas { /* Fix helper layer canvas going off the main canvas when main canvas is vertically thin */ position: absolute; left: 0; top: 0; } .resize-ghost, .component-ghost { pointer-events: none; } .resize-ghost { z-index: 4; } .component-ghost { z-index: 5001; /* 50-5000 reserved for subwindows, which dynamically increase z-index */ } .textbox-editor { color: transparent !important; background: transparent !important; caret-color: black; z-index: 4; /* go above handles and .main-canvas */ outline: none; } /* debug */ /*.textbox-editor:hover { color: rgba(255, 0, 255, 0.5) !important; }*/ .status-area { display: flex; overflow: hidden; white-space: nowrap; cursor: default; } .status-text { flex-basis: auto; flex-grow: 1; flex-shrink: 0; padding-right: 2px; overflow: hidden; } .status-coordinates { flex: 0 0 114px; min-width: 0px; padding-left: 3px; } .hover-halo { pointer-events: none; z-index: 1000000; box-shadow: 0 0 10px yellow, 0 0 3px yellow; } .dwell-indicator { position: fixed; pointer-events: none; z-index: 1000000; } .dwell-indicator::after { content: ""; display: block; position: absolute; background: red; left: 2px; top: 2px; right: 2px; bottom: 2px; } .dwell-indicator:not(.for-release) { background: yellow; } .dwell-indicator.for-release { background: white; } .dwell-indicator:not(.for-release), .dwell-indicator:not(.for-release)::after { border-radius: 50%; } .dwell-indicator.for-release, .dwell-indicator.for-release::after { clip-path: polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%); } .eye-gaze-mode .color-button { width: 25px; height: 25px; } .eye-gaze-mode .colors-component.tall { width: 50px; } .eye-gaze-mode .colors-component.tall .color-box { width: 100%; } .eye-gaze-mode .colors-component.tall .current-colors { width: 100%; } .eye-gaze-mode .colors-component.tall .foreground-color { left: 12px; } .eye-gaze-mode .colors-component.tall .background-color { right: 12px; } .eye-gaze-mode .os-window .window-titlebar { font-size: 2rem; line-height: 1.2; height: 2.8rem; } .eye-gaze-mode .os-window .window-title-area { height: 100%; } .eye-gaze-mode .os-window .window-titlebar button { transform: scale(3); margin-right: 32px; } .eye-gaze-mode .tool-window .window-titlebar button { transform-origin: 0 0; } .eye-gaze-mode .menu-button { padding-right: 10px; padding-left: 10px; } .eye-gaze-mode .menu-button { height: 3rem; line-height: 2.5rem; } .eye-gaze-mode .menu-button, .eye-gaze-mode .menu-button * { font-size: 2rem; } .eye-gaze-mode .menu-item * { font-size: 2rem; line-height: 1.5; } .eye-gaze-mode .menu-item-checkbox-area::after, .eye-gaze-mode .menu-item-submenu-area::after { width: 1.2em; height: 1.2em; mask-size: 1.2em 1.2em; } .eye-gaze-mode .status-area { padding-left: 170px; } .eye-gaze-mode-floating-buttons { position: fixed; bottom: 0; left: 0; transform-origin: bottom left; transform: scale(3); /* They're not within .jspaint currently */ -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .eye-gaze-mode-floating-buttons button { width: 28px; height: 28px; vertical-align: bottom; position: relative; } .eye-gaze-mode-floating-buttons .button-icon { position: absolute; left: 0; top: 0; width: 24px; height: 24px; } .menu-button { /* @TODO: make this part of os-gui */ white-space: nowrap; } .menus { /* @TODO: make this part of os-gui; note that Explorer does overflow differently though */ flex-wrap: wrap; } .component-area { /* for measuring offsetTop/offsetLeft of component elements */ /* (makes it relative to this element) */ position: relative; } .tools-component { height: 273px; align-items: center; padding-left: 4px; padding-right: 2px; display: flex; flex-flow: column; } .tool-options { display: flex; margin-top: 3px; width: 41px; height: 66px; box-sizing: border-box; -moz-box-sizing: border-box; } .tool-options > div { flex: 1; } .tools { display: flex; flex-flow: row wrap; } .tool { display: block !important; box-sizing: border-box; -moz-box-sizing: border-box; position: relative; } .horizontal .component-area { flex-direction: column; } .component { display: flex; } .colors-component { align-items: center; justify-content: center; } .colors-component.wide { height: 47px; } .colors-component.tall { width: 47px; } .palette { display: flex; } .colors-component.wide .palette { flex-flow: row wrap; } .colors-component.tall .palette { flex-flow: column wrap; } .colors-component.wide .color-box, .colors-component.wide .palette { display: flex; flex-direction: row; height: 32px; } .colors-component.tall .color-box, .colors-component.tall .palette { display: flex; flex-direction: column; width: 32px; } .colors-component.wide .current-colors { width: 30px; height: 31px; } .colors-component.tall .current-colors { width: 31px; height: 32px; } .current-colors, .color-button { position: relative; } .foreground-color { position: absolute; left: 2px; top: 4px; } .background-color { position: absolute; right: 3px; bottom: 3px; } .colors-component.tall .foreground-color { left: 4px; top: 3px; } .colors-component.tall .background-color { right: 3px; bottom: 3px; } .colors-component.wide .color-button, .colors-component.wide .color-selection { margin-left: 1px; } .colors-component.tall .color-button, .colors-component.tall .color-selection { margin-top: 1px; } .color-button, .color-selection { display: flex; padding: 0; box-sizing: border-box; width: 15px; height: 15px; border: 0; } .edit-colors-window .color-grid { width: 222px; display: grid; grid-template-columns: repeat(8, 16px); grid-gap: 5px 9px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; margin-left: 8px/*rtl:ignore*/; } .edit-colors-window .swatch { width: 16px; height: 13px; display: flex; } .edit-colors-window .window-content { font-family: Tahoma, sans-serif; font-size: 12px; } .edit-colors-window .swatch { outline: none; /* we'll provide a new focus indicator below */ } .edit-colors-window .swatch.selected { outline: 1px solid black; outline-offset: 0px; } .edit-colors-window .swatch:focus::after { content: ""; display: block; position: absolute; left: 0; top: 0; right: 0; bottom: 0; outline: 1px dotted black; outline-offset: 5px; } .edit-colors-window .window-content .left-right-split { display: flex; flex-flow: row; } .edit-colors-window .window-content .left-side { /* display: flex; flex-flow: column; */ width: 217px; height: 298px; } .edit-colors-window .window-content .right-side { width: 218px; position: relative; padding-top: 7px; padding-left: 10px/*rtl:ignore*/; } .edit-colors-window .window-content .button-group { display: flex; flex-flow: row; } .edit-colors-window .window-content .button-group button { min-width: 66px; margin: 3px; } .edit-colors-window .window-content .define-custom-colors-button, .edit-colors-window .window-content .button-group button:first-of-type { margin-left: 5px/*rtl:ignore*/; } .edit-colors-window .window-content button { height: 23px; box-sizing: border-box; padding: 0; margin-left: 3px/*rtl:ignore*/; } .edit-colors-window .window-content .define-custom-colors-button { margin-top: 13px; width: 210px; } .edit-colors-window .window-content .add-to-custom-colors-button { position: absolute; bottom: 5px; right: 5px/*rtl:ignore*/; width: 213px; } .edit-colors-window .left-side label { display: block; margin-top: 7px; margin-bottom: 5px; margin-left: 5px/*rtl:ignore*/; } .edit-colors-window .left-side label:nth-of-type(2) { margin-top: 18px; margin-bottom: 7px; } .edit-colors-window .luminosity-canvas { margin-left: 15px/*rtl:ignore*/; } .edit-colors-window .result-color-canvas { margin-top: 4px; } /* WET layout code for small viewports and eye gaze mode */ /* could do it cleaner with JavaScript or CSS preprocessor */ @media (max-width: 450px) { .edit-colors-window { overflow: hidden; } .edit-colors-window.defining-custom-colors .left-side { /* display: none !important; */ /* this element is determining the height */ width: 0; visibility: hidden; } .edit-colors-window:not(.defining-custom-colors) .right-side { display: none !important; } } /* WET layout code for small viewports and eye gaze mode */ .eye-gaze-mode .edit-colors-window { overflow: hidden; } .eye-gaze-mode .edit-colors-window.defining-custom-colors .left-side { /* display: none !important; */ /* this element is determining the height */ width: 0; visibility: hidden; } .eye-gaze-mode .edit-colors-window:not(.defining-custom-colors) .right-side { display: none !important; } .save-as .window-content > form > div { display: flex; flex-direction: column; } .save-as .window-content > form > div > label { display: flex; flex-direction: row; justify-content: space-between; align-items: center; margin-left: 10px; } .save-as .window-content > form > div > label > input, .save-as .window-content > form > div > label > select { width: calc(100vw - 220px); max-width: 230px; float: right; margin: 5px; box-sizing: border-box; } .save-as .window-content > form > div > label:not(:first-of-type) { margin-top: 8px; } .save-as .window-content { padding-top: 10px; padding-bottom: 10px; } .save-as .button-group button { margin: 5px; } .font-box { display: flex; flex-flow: row; align-items: center; margin: 4px 7px; /* not measured, just guessed */ gap: 10px; } .canvas-area { flex: 1; display: block !important; position: relative; overflow: auto; padding: 3px; direction: ltr/*rtl:ignore*/; } .main-canvas { position: absolute; z-index: 2; } .canvas-area .handle { position: absolute; width: 3px; height: 3px; z-index: 1; pointer-events: none; /* important for eye gaze mode */ } .grab-region { /* the grab-region make handles way easier to grab by extending outside the visual representation of the handle */ position: absolute; /*background: rgba(255, 0, 0, 0.5);*/ /* debug */ } /* .grab-region.is-middle { */ /*background: rgba(255, 255, 0, 0.5);*/ /* debug */ /* } */ .textbox::before { /* allow dragging textbox */ /* In mspaint the border drawn around selections and textboxes extends out from them, centered on the pixels bordering the contents, which makes it more reasonable to have the border be a draggable thing. I'm making the draggable area outside the border for now. */ content: ""; pointer-events: all; /* @TODO: maybe don't have a blanket pointer-events: none; on pseudo elements */ display: block; position: absolute; left: -10px; right: -10px; top: -10px; bottom: -10px; /*background: orange;*/ /* debug */ } .window-content .button-group { display: flex; flex: 0 0 auto; flex-flow: column; } .window-content .button-group > button { min-width: 80px; padding: 3px 5px; white-space: nowrap; } .window-content > form { display: flex; flex-flow: row; } .window:not(.edit-colors-window) .window-content > form { direction: ltr; } .horizontal-buttons .window-content > form { flex-flow: column; } .horizontal-buttons .window-content > form > .button-group { display: flex; flex-flow: row; justify-content: flex-end; gap: 5px; margin: 5px; margin-bottom: 10px; } .horizontal-buttons .window-content > form > div:first-child { padding: 5px; } .stretch-and-skew .window-content, .flip-and-rotate .window-content, .convert-to-black-and-white .window-content, .component-window .window-content { direction: ltr; } .dialog-window:not(.horizontal-buttons):not(.edit-colors-window) .window-content { padding: 10px; } .dialog-window:not(.horizontal-buttons):not(.edit-colors-window) .window-content .button-group { padding-left: 10px; display: flex; flex-direction: column; gap: 5px; } .flip-and-rotate fieldset { width: 200px; } .flip-and-rotate fieldset > label { display: flex !important; /* overriding `.window:not(.edit-colors-window) .window-content label` */ } .flip-and-rotate input:disabled { /* pointer events already can't get received on disabled input elements, but this lets them be received on a parent, in order to enable the element */ pointer-events: none; } .flip-and-rotate .sub-options { padding-left: 30px; } .flip-and-rotate .radio-wrapper { width: fit-content; } /* Fix label ordering for RTL layout (display inline labels can get super out of order with the checkboxes/inputs - very confusing!) */ .window:not(.edit-colors-window):not(.tracky-mouse-window) .window-content label { display: inline-flex; flex-direction: row; direction: ltr; } .attributes-window .window-content { direction: ltr; } .attributes-window table { margin-bottom: 5px; } .attributes-window input[type="number"] { margin-left: 5px; /* separate label from input for Width and Height fields */ } .attributes-window table ~ label ~ label { margin-left: 10px; /* separate Width and Height fields */ } .attributes-window fieldset { margin-top: 5px; } .attributes-window fieldset .fieldset-body { display: grid; grid-template-columns: calc(80px * 2) 80px; } .attributes-window fieldset:first-of-type .fieldset-body { grid-template-columns: 80px 80px 80px; } .custom-zoom-window .current-zoom { margin: 10px 15px; } .custom-zoom-window fieldset { margin: 8px; padding: 0; } .custom-zoom-window .fieldset-body { display: flex; flex-flow: column wrap; width: 240px; height: 70px; padding: 12px 5px; row-gap: 10px; } .custom-zoom-window bdi { margin: 0 10px; } .custom-zoom-window input[name='really-custom-zoom-input'] { width: 50px; } /* @TODO: part of os-gui */ .os-window { display: flex; flex-direction: column; } /* @TODO: part of os-gui */ .os-window .window-content { flex: 1; } .help-window .window-content { display: flex; flex-flow: column; } .help-window .main { flex: 1; display: flex; flex-flow: row; height: 0; /* fixes an overflow issue with small window height, where the iframe and contents tree would be sized based on the intrinsic height of the contents tree */ } .help-window .toolbar button { width: 55px; height: 40px; padding: 0; } .help-window .toolbar button span { display: inline-flex; position: absolute; bottom: 0; left: 0; right: 0; /* flex centering + preventing overflow wrap means the font can be too big and it'll still stay centered */ font-size: 12px; white-space: pre; justify-content: center; } .help-window .toolbar button { position: relative; } .help-window .toolbar button .icon { width: 100%; height: 100%; position: absolute; top: 0; left: 0; } .help-window .toolbar button .icon { background-image: url("../images/help-viewer-toolbar-icons-grayscale.png"); } .help-window .toolbar button:not([disabled]):hover .icon { background-image: url("../images/help-viewer-toolbar-icons.png"); } .help-window .toolbar button[disabled] .icon { filter: saturate(0%) opacity(50%); /* fallback */ filter: url("#disabled-inset-filter"); } .help-window .contents { background: white; background: var(--Window); color: var(--WindowText); flex-basis: 300px; /* normally the default is 200px, but that leaves a scrollbar and we don't have rollover viewing of longer titles (@TODO) */ flex-grow: 0; flex-shrink: 0; overflow: auto; box-sizing: border-box; } .help-window ul { margin: 0; padding: 0; } .help-window li { display: block; white-space: nowrap; } .help-window .item { display: inline-block; } .help-window .folder:not(.expanded) ul { display: none; } .help-window iframe { flex: 1; width: 0; } .help-window li ul { /* Help content is not currently translated, so it's all in English */ padding-left: 16px/*rtl:ignore*/; } .help-window li:before { content: ""; display: inline-block; vertical-align: middle; width: 16px; height: 16px; background-position: 0 0; margin-right: 2px; } .help-window .folder.expanded:before { background-position: -16px 0; } .help-window .page:before { background-position: -32px 0; } .dragging iframe { pointer-events: none; } .storage-manager table { max-height: 400px; overflow: auto; display: block; } .storage-manager .thumbnail-container { width: 64px; height: 64px; display: flex; align-items: center; justify-content: center; } .storage-manager .thumbnail-container > img { max-width: 64px; max-height: 64px; flex: 0 0 auto; } .storage-manager .thumbnail-container, .storage-manager p { margin: 5px; } .storage-manager .remove-button { margin-left: 15px; } .history-window .window-content { direction: ltr; } .history-view { width: 500px; height: 500px; max-width: calc(100vw - 10px); max-height: calc(100vh - 100px); overflow: auto; position: relative; /* needed for offsetTop to work relative to the top of the list (rather than the window) */ direction: ltr; } .history-entry { cursor: pointer; padding: 5px; display: flex; } .history-entry-icon-area { width: 16px; height: 16px; margin-right: 6px; } .history-entry.current { font-weight: bold; } .history-entry:not(.current):not(.ancestor-of-current) { color: gray; } .history-entry:hover:hover:hover { /* specificity hack vs :not()s */ color: #0000ff; text-decoration: underline; } ::before, ::after { pointer-events: none; } .cursor-bully * { cursor: inherit !important; } #about-paint-header { display: flex; flex-direction: row; margin: 0; margin-top: 30px; margin-bottom: 10px; } #jspaint-project-name { margin-left: 10px; margin-right: 8px; white-space: nowrap; } #jspaint-version { /* @TODO: separate into a shared.css? (not really layout!) */ font-size: 0.6em; color: #7b7b7b; } #maybe-outdated-line { font-style: italic; height: 2em; } #view-project-news, #close-about-paint { margin: auto; /* for right-alignment AND avoiding stretching to height of the container */ margin-right: 0; width: 100px; min-height: 2.2em; } #close-about-paint { float: right; margin-bottom: 10px; } #news { max-height: 500px; overflow: auto; -webkit-user-select: text; -moz-user-select: text; -ms-user-select: text; user-select: text; cursor: auto; } @media (max-height: 550px) { #news { max-height: calc(100vh - 50px); } } .news-indicator { display: flex; font-family: sans-serif; } .news-indicator > img { margin-right: 5px; } @media (max-width: 550px) { .news-indicator > img { position: absolute; right: 0; bottom: 0; } .news-indicator .marquee { display: none; } } .marquee { overflow: hidden; text-decoration: inherit; } .marquee span { display: inline-block; white-space: nowrap; width: var(--text-width); text-shadow: var(--text-width) 0 currentColor, calc(var(--text-width) * 2) 0 currentColor, calc(var(--text-width) * 3) 0 currentColor, calc(var(--text-width) * 4) 0 currentColor; will-change: transform; animation: marquee var(--animation-duration) linear infinite; animation-play-state: paused; text-decoration: inherit; } .marquee:hover span { animation-play-state: running; } @keyframes marquee { 0% { transform: translateX(0); } 100% { transform: translateX(-100%)/*rtl:ignore*/; } } /* On MacOs: System Preferences > Accessibility > Display > Reduce motion */ @media (prefers-reduced-motion: reduce) { .marquee span { animation: none; text-shadow: none; width: auto; display: block; line-height: 1.5; text-align: center; white-space: normal; } } /* For Safari on iPad, Fullscreen mode overlays the system bar, completely obscuring our menu bar. */ /* This adds a spacer to the top of the page, styled with the title bar gradient, to prevent that. */ /* Only show this if the device is tall enough that this won't cause any problems (fullscreen is important for fitting the color palette on the screen on some phones) */ :root { --ios-title-bar-height: 21px; /* this is what looks good to my eye, there's not a hard line in the overlay, just text/symbols, and I didn't measure the system bar in a case where it has a background */ /* hope it doesn't change! */ } @media (min-height: 450px) { .ios.fullscreen .jspaint { padding-top: var(--ios-title-bar-height); } .ios.fullscreen .jspaint::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: var(--ios-title-bar-height); } /* That works, but now the exit fullscreen button (which we also can't change) obscures the menu bar. */ /* So center the menu bar contents, that'll look nice. */ /* .ios.fullscreen .menus { justify-content: center; } /* Ugh no, then the three dots button (multitasking menu) at the top of the system menu steals taps from the menu bar. */ /* Just shift it over by a fixed amount. */ /* This lets it sit in between the exit button and the three dots button, at least on a 9th generation iPad with software version 15.0.2 (this is a very fragile workaround) */ .ios.fullscreen .menus { padding-left: 100px; } /* Also nudge the tools downward to avoid the exit fullscreen button */ /* (not if it's in a window, only if it's docked) */ /* (I considered using `.ios.fullscreen .horizontal > .component-area:first-child` so that it's not affected by the transform in Eye Gaze Mode, but it wants tweaking in that case anyways due to the menu bar being bigger */ .ios.fullscreen .component-area .tools-component { padding-top: 40px; } /* Not so much in Eye Gaze Mode, where it's scaled with transform, and where the menu bar takes more of the space reserved for the exit fullscreen button */ .ios.fullscreen .eye-gaze-mode .component-area .tools-component { padding-top: 10px; } } select.inset-deep { background-position: top 0px right 0px; padding-right: 16px; } progress { width: 291px; height: 20px; box-sizing: border-box; } details pre { margin: 0; margin-top: 5px; -webkit-user-select: text; -moz-user-select: text; -ms-user-select: text; user-select: text; } details { margin-top: 8px; } summary { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: pointer; } /* OVERRIDES */ .extras-menu-button { display: none; } .menu-item[aria-label="Upload To Imgur"] { display: none; } .menu-item[aria-label="Save As"] { display: none; } .menu-item[aria-label="Manage Storage"] { display: none; } .menu-item[aria-label="Manage Storage"] + .menu-row { display: none; } .menu-item[aria-label="Recent File"] { display: none; } .menu-item[aria-label="Recent File"] + .menu-row { display: none; } .menu-item[aria-label="Help Topics"] { display: none; } .menu-item[aria-label="Help Topics"] + .menu-row { display: none; }