mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-23 18:17:38 +00:00
Use CSS variables for sprite images
This commit is contained in:
parent
90bc65830c
commit
b067ac5cb4
12 changed files with 729 additions and 414 deletions
File diff suppressed because one or more lines are too long
|
|
@ -23,6 +23,36 @@
|
|||
outline: 0;
|
||||
}
|
||||
|
||||
#webamp [style*="--active-background"]:active {
|
||||
background-image: var(--active-background);
|
||||
}
|
||||
#webamp [style*="--base-background"] {
|
||||
background-image: var(--base-background);
|
||||
}
|
||||
#webamp [style*="--base-background"] {
|
||||
background-image: var(--base-background);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ideally we would collapse these next four down into two rules that each have
|
||||
* multiple comma separated selectors. However, `::-moz-range-thumb`
|
||||
* will break the rule in non-Firefox browsers, so we just make each
|
||||
* selector it's own rule.
|
||||
*/
|
||||
|
||||
#webamp [style*="--thumb-background"]::-webkit-slider-thumb {
|
||||
background-image: var(--thumb-background);
|
||||
}
|
||||
#webamp [style*="--thumb-background"]::-moz-range-thumb {
|
||||
background-image: var(--thumb-background);
|
||||
}
|
||||
#webamp [style*="--active-thumb-background"]:active::-webkit-slider-thumb {
|
||||
background-image: var(--active-thumb-background);
|
||||
}
|
||||
#webamp [style*="--active-thumb-background"]:active::-moz-range-thumb {
|
||||
background-image: var(--active-thumb-background);
|
||||
}
|
||||
|
||||
/* Range input css reset */
|
||||
#webamp input[type="range"] {
|
||||
-webkit-appearance: none;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,22 @@
|
|||
import React from "react";
|
||||
|
||||
import * as Actions from "../../actionCreators";
|
||||
import { useActionCreator } from "../../hooks";
|
||||
import { useActionCreator, useSprite } from "../../hooks";
|
||||
|
||||
const Eject = React.memo(() => {
|
||||
const openMediaFileDialog = useActionCreator(Actions.openMediaFileDialog);
|
||||
return <div id="eject" onClick={openMediaFileDialog} title="Open File(s)" />;
|
||||
const spriteStyle = useSprite({
|
||||
base: "MAIN_EJECT_BUTTON",
|
||||
size: "MAIN_EJECT_BUTTON",
|
||||
active: "MAIN_EJECT_BUTTON_ACTIVE",
|
||||
});
|
||||
return (
|
||||
<div
|
||||
style={{ ...spriteStyle, position: "absolute", top: 89, left: 136 }}
|
||||
onClick={openMediaFileDialog}
|
||||
title="Open File(s)"
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default Eject;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React from "react";
|
|||
|
||||
import Balance from "../Balance";
|
||||
import * as Selectors from "../../selectors";
|
||||
import { useTypedSelector } from "../../hooks";
|
||||
import { useSprite, useTypedSelector } from "../../hooks";
|
||||
|
||||
export const offsetFromBalance = (balance: number): number => {
|
||||
const percent = Math.abs(balance) / 100;
|
||||
|
|
@ -13,10 +13,23 @@ export const offsetFromBalance = (balance: number): number => {
|
|||
|
||||
const MainBalance = React.memo(() => {
|
||||
const balance = useTypedSelector(Selectors.getBalance);
|
||||
const spriteStyle = useSprite({
|
||||
base: "MAIN_BALANCE_BACKGROUND",
|
||||
thumb: "MAIN_BALANCE_THUMB",
|
||||
activeThumb: "MAIN_BALANCE_THUMB_ACTIVE",
|
||||
});
|
||||
return (
|
||||
<Balance
|
||||
id="balance"
|
||||
style={{ backgroundPosition: `0 -${offsetFromBalance(balance)}px` }}
|
||||
style={{
|
||||
...spriteStyle,
|
||||
left: 177,
|
||||
top: 57,
|
||||
height: 13,
|
||||
width: 38,
|
||||
position: "absolute",
|
||||
backgroundPosition: `0 -${offsetFromBalance(balance)}px`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const MainVolume = React.memo(() => {
|
|||
};
|
||||
return (
|
||||
<div id="volume" style={style}>
|
||||
<Volume />
|
||||
<Volume style={{ background: "none" }} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import * as Selectors from "../../selectors";
|
|||
import "../../../css/main-window.css";
|
||||
import { FilePicker } from "../../types";
|
||||
import FocusTarget from "../FocusTarget";
|
||||
import { useActionCreator, useTypedSelector } from "../../hooks";
|
||||
import { useActionCreator, useSprite, useTypedSelector } from "../../hooks";
|
||||
|
||||
interface Props {
|
||||
analyser: AnalyserNode;
|
||||
|
|
@ -52,6 +52,7 @@ const MainWindow = React.memo(({ analyser, filePickers }: Props) => {
|
|||
const doubled = useTypedSelector(Selectors.getDoubled);
|
||||
const llama = useTypedSelector(Selectors.getLlamaMode);
|
||||
const working = useTypedSelector(Selectors.getWorking);
|
||||
const style = useSprite({ base: "MAIN_WINDOW_BACKGROUND" });
|
||||
|
||||
const className = classnames({
|
||||
window: true,
|
||||
|
|
@ -74,6 +75,7 @@ const MainWindow = React.memo(({ analyser, filePickers }: Props) => {
|
|||
|
||||
return (
|
||||
<DropTarget
|
||||
style={style}
|
||||
id="main-window"
|
||||
windowId={WINDOWS.MAIN}
|
||||
className={className}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { SkinImages } from "../types";
|
|||
import { createSelector } from "reselect";
|
||||
import Css from "./Css";
|
||||
import ClipPaths from "./ClipPaths";
|
||||
import { imageVarName, cursorVarName } from "../utils";
|
||||
|
||||
const CSS_PREFIX = "#webamp";
|
||||
|
||||
|
|
@ -34,28 +35,37 @@ const FALLBACKS: { [key: string]: string } = {
|
|||
MAIN_BALANCE_THUMB_ACTIVE: "MAIN_VOLUME_THUMB_SELECTED",
|
||||
};
|
||||
|
||||
const getCssRules = createSelector(
|
||||
const getCssVars = createSelector(
|
||||
Selectors.getSkinImages,
|
||||
Selectors.getSkinCursors,
|
||||
Selectors.getSkinLetterWidths,
|
||||
Selectors.getSkinRegion,
|
||||
(skinImages, skinCursors, skinGenLetterWidths, skinRegion): string | null => {
|
||||
(skinImages, skinCursors): string | null => {
|
||||
if (!skinImages || !skinCursors) {
|
||||
return null;
|
||||
}
|
||||
const cssRules = [];
|
||||
Object.keys(imageSelectors).forEach((imageName) => {
|
||||
const imageUrl =
|
||||
skinImages[imageName] || skinImages[FALLBACKS[imageName]];
|
||||
if (imageUrl) {
|
||||
imageSelectors[imageName].forEach((selector) => {
|
||||
cssRules.push(
|
||||
`${CSS_PREFIX} ${selector} {background-image: url(${imageUrl})}`
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
const variableRules = [
|
||||
...Object.entries(skinImages).map(([imageName, value]) => [
|
||||
imageVarName(imageName),
|
||||
value || skinImages[FALLBACKS[imageName]],
|
||||
]),
|
||||
...Object.entries(skinCursors).map(([imageName, value]) => [
|
||||
cursorVarName(imageName),
|
||||
value,
|
||||
]),
|
||||
];
|
||||
|
||||
const rules = variableRules
|
||||
.filter(([, image]) => image != null)
|
||||
.map(([name, image]) => `${name}: url(${image})`)
|
||||
.join(";\n ");
|
||||
|
||||
return `${CSS_PREFIX} {\n ${rules}\n}`;
|
||||
}
|
||||
);
|
||||
|
||||
const getLetterWidths = createSelector(
|
||||
Selectors.getSkinLetterWidths,
|
||||
(skinGenLetterWidths) => {
|
||||
const cssRules: string[] = [];
|
||||
if (skinGenLetterWidths != null) {
|
||||
LETTERS.forEach((letter) => {
|
||||
const width = skinGenLetterWidths[`GEN_TEXT_${letter}`];
|
||||
|
|
@ -69,21 +79,43 @@ const getCssRules = createSelector(
|
|||
);
|
||||
});
|
||||
}
|
||||
return cssRules.join("\n");
|
||||
}
|
||||
);
|
||||
|
||||
const getCssRules = createSelector(
|
||||
Selectors.getSkinImages,
|
||||
Selectors.getSkinRegion,
|
||||
(skinImages, skinRegion): string => {
|
||||
const cssRules = [];
|
||||
// DEPRECATED: All of these should be replaced with calls to `useSprite` in
|
||||
// the component that actually renders the element.
|
||||
Object.entries(imageSelectors).forEach(([imageName, selectors]) => {
|
||||
selectors.forEach((selector) => {
|
||||
// Ideally we would collapse this down into a single rule that has
|
||||
// multipel comma separated selectors. However, `::-moz-range-thumb`
|
||||
// will break the rule in non-Firefox browsers, so we just make each
|
||||
// selector it's own rule.
|
||||
cssRules.push(
|
||||
`${CSS_PREFIX} ${selector} {\n background-image: var(${imageVarName(
|
||||
imageName
|
||||
)});\n}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Object.keys(cursorSelectors).forEach((cursorName) => {
|
||||
const cursorUrl = skinCursors[cursorName];
|
||||
if (cursorUrl) {
|
||||
cursorSelectors[cursorName].forEach((selector) => {
|
||||
cssRules.push(
|
||||
`${
|
||||
// TODO: Fix this hack
|
||||
// Maybe our CSS name spacing should be based on some other class/id
|
||||
// than the one we use for defining the main div.
|
||||
// That way it could be shared by both the player and the context menu.
|
||||
selector.startsWith("#webamp-context-menu") ? "" : CSS_PREFIX
|
||||
} ${selector} {cursor: url(${cursorUrl}), auto}`
|
||||
);
|
||||
});
|
||||
}
|
||||
cursorSelectors[cursorName].forEach((selector) => {
|
||||
cssRules.push(
|
||||
`${
|
||||
// TODO: Fix this hack
|
||||
// Maybe our CSS name spacing should be based on some other class/id
|
||||
// than the one we use for defining the main div.
|
||||
// That way it could be shared by both the player and the context menu.
|
||||
selector.startsWith("#webamp-context-menu") ? "" : CSS_PREFIX
|
||||
} ${selector} {cursor: var(${cursorVarName(cursorName)}), auto}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
if (numExIsUsed(skinImages)) {
|
||||
|
|
@ -119,13 +151,15 @@ const getClipPaths = createSelector(Selectors.getSkinRegion, (skinRegion) => {
|
|||
|
||||
export default function Skin() {
|
||||
const cssRules = useTypedSelector(getCssRules);
|
||||
const cssVars = useTypedSelector(getCssVars);
|
||||
const letterWidths = useTypedSelector(getLetterWidths);
|
||||
const clipPaths = useTypedSelector(getClipPaths);
|
||||
if (cssRules == null) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Css id="webamp-skin">{cssRules}</Css>
|
||||
<Css id="webamp-style">{cssRules}</Css>
|
||||
{cssVars != null && (
|
||||
<Css id="webamp-skin">{`${cssVars} ${letterWidths}`}</Css>
|
||||
)}
|
||||
<ClipPaths>{clipPaths}</ClipPaths>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
useLayoutEffect,
|
||||
useRef,
|
||||
} from "react";
|
||||
import { spritesByName, SpriteName } from "./skinSprites";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import * as Utils from "./utils";
|
||||
import { Action, Thunk, AppState } from "./types";
|
||||
|
|
@ -171,3 +172,34 @@ export function useActionCreator<T extends (...args: any[]) => Action | Thunk>(
|
|||
export function useTypedDispatch(): (action: Action | Thunk) => void {
|
||||
return useDispatch();
|
||||
}
|
||||
|
||||
type SpriteConfig = {
|
||||
base?: SpriteName;
|
||||
active?: SpriteName;
|
||||
thumb?: SpriteName;
|
||||
activeThumb?: SpriteName;
|
||||
size?: SpriteName;
|
||||
};
|
||||
|
||||
export function useSprite(options: SpriteConfig): React.CSSProperties {
|
||||
const style: React.CSSProperties = {};
|
||||
(["base", "active", "thumb", "activeThumb"] as const).forEach((name) => {
|
||||
const spriteName = options[name];
|
||||
if (spriteName != null) {
|
||||
// Ideally we could use `{backgroundImage: "var(varName)"}` for `base`.
|
||||
// Sadly, that overrides any `:active` styles. So instead we just use
|
||||
// this CSS variable hack for all sprite backgrounds.
|
||||
// @ts-ignore
|
||||
style[`--${name}-background`] = `var(${Utils.imageVarName(spriteName)})`;
|
||||
}
|
||||
});
|
||||
if (options.size != null) {
|
||||
const sprite = spritesByName[options.size];
|
||||
if (sprite == null) {
|
||||
throw new Error(`Could not find sprite style for "${options.size}"`);
|
||||
}
|
||||
style.height = sprite.height;
|
||||
style.width = sprite.width;
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,9 +25,6 @@ export const imageSelectors: Selectors = {
|
|||
MAIN_STOP_BUTTON_ACTIVE: [".actions #stop:active"],
|
||||
MAIN_NEXT_BUTTON: [".actions #next"],
|
||||
MAIN_NEXT_BUTTON_ACTIVE: [".actions #next:active"],
|
||||
MAIN_EJECT_BUTTON: ["#eject"],
|
||||
MAIN_EJECT_BUTTON_ACTIVE: ["#eject:active"],
|
||||
MAIN_WINDOW_BACKGROUND: ["#main-window"],
|
||||
MAIN_STEREO: [".media-info #stereo", ".stop .media-info #stereo.selected"],
|
||||
MAIN_STEREO_SELECTED: [".media-info #stereo.selected"],
|
||||
MAIN_MONO: [".media-info #mono", ".stop .media-info #mono.selected"],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,215 @@
|
|||
import { UTF8_ELLIPSIS } from "./constants";
|
||||
|
||||
export type SpriteName = string;
|
||||
export type SpriteName =
|
||||
| "MAIN_BALANCE_BACKGROUND"
|
||||
| "MAIN_BALANCE_THUMB"
|
||||
| "MAIN_BALANCE_THUMB_ACTIVE"
|
||||
| "MAIN_PREVIOUS_BUTTON"
|
||||
| "MAIN_PREVIOUS_BUTTON_ACTIVE"
|
||||
| "MAIN_PLAY_BUTTON"
|
||||
| "MAIN_PLAY_BUTTON_ACTIVE"
|
||||
| "MAIN_PAUSE_BUTTON"
|
||||
| "MAIN_PAUSE_BUTTON_ACTIVE"
|
||||
| "MAIN_STOP_BUTTON"
|
||||
| "MAIN_STOP_BUTTON_ACTIVE"
|
||||
| "MAIN_NEXT_BUTTON"
|
||||
| "MAIN_NEXT_BUTTON_ACTIVE"
|
||||
| "MAIN_EJECT_BUTTON"
|
||||
| "MAIN_EJECT_BUTTON_ACTIVE"
|
||||
| "MAIN_WINDOW_BACKGROUND"
|
||||
| "MAIN_STEREO"
|
||||
| "MAIN_STEREO_SELECTED"
|
||||
| "MAIN_MONO"
|
||||
| "MAIN_MONO_SELECTED"
|
||||
| "NO_MINUS_SIGN"
|
||||
| "MINUS_SIGN"
|
||||
| "DIGIT_0"
|
||||
| "DIGIT_1"
|
||||
| "DIGIT_2"
|
||||
| "DIGIT_3"
|
||||
| "DIGIT_4"
|
||||
| "DIGIT_5"
|
||||
| "DIGIT_6"
|
||||
| "DIGIT_7"
|
||||
| "DIGIT_8"
|
||||
| "DIGIT_9"
|
||||
| "NO_MINUS_SIGN_EX"
|
||||
| "MINUS_SIGN_EX"
|
||||
| "DIGIT_0_EX"
|
||||
| "DIGIT_1_EX"
|
||||
| "DIGIT_2_EX"
|
||||
| "DIGIT_3_EX"
|
||||
| "DIGIT_4_EX"
|
||||
| "DIGIT_5_EX"
|
||||
| "DIGIT_6_EX"
|
||||
| "DIGIT_7_EX"
|
||||
| "DIGIT_8_EX"
|
||||
| "DIGIT_9_EX"
|
||||
| "MAIN_PLAYING_INDICATOR"
|
||||
| "MAIN_PAUSED_INDICATOR"
|
||||
| "MAIN_STOPPED_INDICATOR"
|
||||
| "MAIN_NOT_WORKING_INDICATOR"
|
||||
| "MAIN_WORKING_INDICATOR"
|
||||
| "PLAYLIST_TOP_TILE"
|
||||
| "PLAYLIST_TOP_LEFT_CORNER"
|
||||
| "PLAYLIST_TITLE_BAR"
|
||||
| "PLAYLIST_TOP_RIGHT_CORNER"
|
||||
| "PLAYLIST_TOP_TILE_SELECTED"
|
||||
| "PLAYLIST_TOP_LEFT_SELECTED"
|
||||
| "PLAYLIST_TITLE_BAR_SELECTED"
|
||||
| "PLAYLIST_TOP_RIGHT_CORNER_SELECTED"
|
||||
| "PLAYLIST_LEFT_TILE"
|
||||
| "PLAYLIST_RIGHT_TILE"
|
||||
| "PLAYLIST_BOTTOM_TILE"
|
||||
| "PLAYLIST_BOTTOM_LEFT_CORNER"
|
||||
| "PLAYLIST_BOTTOM_RIGHT_CORNER"
|
||||
| "PLAYLIST_VISUALIZER_BACKGROUND"
|
||||
| "PLAYLIST_SHADE_BACKGROUND"
|
||||
| "PLAYLIST_SHADE_BACKGROUND_LEFT"
|
||||
| "PLAYLIST_SHADE_BACKGROUND_RIGHT"
|
||||
| "PLAYLIST_SHADE_BACKGROUND_RIGHT_SELECTED"
|
||||
| "PLAYLIST_SCROLL_HANDLE_SELECTED"
|
||||
| "PLAYLIST_SCROLL_HANDLE"
|
||||
| "PLAYLIST_ADD_URL"
|
||||
| "PLAYLIST_ADD_URL_SELECTED"
|
||||
| "PLAYLIST_ADD_DIR"
|
||||
| "PLAYLIST_ADD_DIR_SELECTED"
|
||||
| "PLAYLIST_ADD_FILE"
|
||||
| "PLAYLIST_ADD_FILE_SELECTED"
|
||||
| "PLAYLIST_REMOVE_ALL"
|
||||
| "PLAYLIST_REMOVE_ALL_SELECTED"
|
||||
| "PLAYLIST_CROP"
|
||||
| "PLAYLIST_CROP_SELECTED"
|
||||
| "PLAYLIST_REMOVE_SELECTED"
|
||||
| "PLAYLIST_REMOVE_SELECTED_SELECTED"
|
||||
| "PLAYLIST_REMOVE_MISC"
|
||||
| "PLAYLIST_REMOVE_MISC_SELECTED"
|
||||
| "PLAYLIST_INVERT_SELECTION"
|
||||
| "PLAYLIST_INVERT_SELECTION_SELECTED"
|
||||
| "PLAYLIST_SELECT_ZERO"
|
||||
| "PLAYLIST_SELECT_ZERO_SELECTED"
|
||||
| "PLAYLIST_SELECT_ALL"
|
||||
| "PLAYLIST_SELECT_ALL_SELECTED"
|
||||
| "PLAYLIST_SORT_LIST"
|
||||
| "PLAYLIST_SORT_LIST_SELECTED"
|
||||
| "PLAYLIST_FILE_INFO"
|
||||
| "PLAYLIST_FILE_INFO_SELECTED"
|
||||
| "PLAYLIST_MISC_OPTIONS"
|
||||
| "PLAYLIST_MISC_OPTIONS_SELECTED"
|
||||
| "PLAYLIST_NEW_LIST"
|
||||
| "PLAYLIST_NEW_LIST_SELECTED"
|
||||
| "PLAYLIST_SAVE_LIST"
|
||||
| "PLAYLIST_SAVE_LIST_SELECTED"
|
||||
| "PLAYLIST_LOAD_LIST"
|
||||
| "PLAYLIST_LOAD_LIST_SELECTED"
|
||||
| "PLAYLIST_ADD_MENU_BAR"
|
||||
| "PLAYLIST_REMOVE_MENU_BAR"
|
||||
| "PLAYLIST_SELECT_MENU_BAR"
|
||||
| "PLAYLIST_MISC_MENU_BAR"
|
||||
| "PLAYLIST_LIST_BAR"
|
||||
| "PLAYLIST_CLOSE_SELECTED"
|
||||
| "PLAYLIST_COLLAPSE_SELECTED"
|
||||
| "PLAYLIST_EXPAND_SELECTED"
|
||||
| "EQ_SHADE_BACKGROUND_SELECTED"
|
||||
| "EQ_SHADE_BACKGROUND"
|
||||
| "EQ_SHADE_VOLUME_SLIDER_LEFT"
|
||||
| "EQ_SHADE_VOLUME_SLIDER_CENTER"
|
||||
| "EQ_SHADE_VOLUME_SLIDER_RIGHT"
|
||||
| "EQ_SHADE_BALANCE_SLIDER_LEFT"
|
||||
| "EQ_SHADE_BALANCE_SLIDER_CENTER"
|
||||
| "EQ_SHADE_BALANCE_SLIDER_RIGHT"
|
||||
| "EQ_MAXIMIZE_BUTTON_ACTIVE"
|
||||
| "EQ_MINIMIZE_BUTTON_ACTIVE"
|
||||
| "EQ_CLOSE_BUTTON"
|
||||
| "EQ_CLOSE_BUTTON_ACTIVE"
|
||||
| "EQ_WINDOW_BACKGROUND"
|
||||
| "EQ_TITLE_BAR"
|
||||
| "EQ_TITLE_BAR_SELECTED"
|
||||
| "EQ_SLIDER_BACKGROUND"
|
||||
| "EQ_SLIDER_THUMB"
|
||||
| "EQ_SLIDER_THUMB_SELECTED"
|
||||
| "EQ_ON_BUTTON"
|
||||
| "EQ_ON_BUTTON_DEPRESSED"
|
||||
| "EQ_ON_BUTTON_SELECTED"
|
||||
| "EQ_ON_BUTTON_SELECTED_DEPRESSED"
|
||||
| "EQ_AUTO_BUTTON"
|
||||
| "EQ_AUTO_BUTTON_DEPRESSED"
|
||||
| "EQ_AUTO_BUTTON_SELECTED"
|
||||
| "EQ_AUTO_BUTTON_SELECTED_DEPRESSED"
|
||||
| "EQ_GRAPH_BACKGROUND"
|
||||
| "EQ_GRAPH_LINE_COLORS"
|
||||
| "EQ_PRESETS_BUTTON"
|
||||
| "EQ_PRESETS_BUTTON_SELECTED"
|
||||
| "EQ_PREAMP_LINE"
|
||||
| "MAIN_POSITION_SLIDER_BACKGROUND"
|
||||
| "MAIN_POSITION_SLIDER_THUMB"
|
||||
| "MAIN_POSITION_SLIDER_THUMB_SELECTED"
|
||||
| "MAIN_SHUFFLE_BUTTON"
|
||||
| "MAIN_SHUFFLE_BUTTON_DEPRESSED"
|
||||
| "MAIN_SHUFFLE_BUTTON_SELECTED"
|
||||
| "MAIN_SHUFFLE_BUTTON_SELECTED_DEPRESSED"
|
||||
| "MAIN_REPEAT_BUTTON"
|
||||
| "MAIN_REPEAT_BUTTON_DEPRESSED"
|
||||
| "MAIN_REPEAT_BUTTON_SELECTED"
|
||||
| "MAIN_REPEAT_BUTTON_SELECTED_DEPRESSED"
|
||||
| "MAIN_EQ_BUTTON"
|
||||
| "MAIN_EQ_BUTTON_SELECTED"
|
||||
| "MAIN_EQ_BUTTON_DEPRESSED"
|
||||
| "MAIN_EQ_BUTTON_DEPRESSED_SELECTED"
|
||||
| "MAIN_PLAYLIST_BUTTON"
|
||||
| "MAIN_PLAYLIST_BUTTON_SELECTED"
|
||||
| "MAIN_PLAYLIST_BUTTON_DEPRESSED"
|
||||
| "MAIN_PLAYLIST_BUTTON_DEPRESSED_SELECTED"
|
||||
| "MAIN_TITLE_BAR"
|
||||
| "MAIN_TITLE_BAR_SELECTED"
|
||||
| "MAIN_EASTER_EGG_TITLE_BAR"
|
||||
| "MAIN_EASTER_EGG_TITLE_BAR_SELECTED"
|
||||
| "MAIN_OPTIONS_BUTTON"
|
||||
| "MAIN_OPTIONS_BUTTON_DEPRESSED"
|
||||
| "MAIN_MINIMIZE_BUTTON"
|
||||
| "MAIN_MINIMIZE_BUTTON_DEPRESSED"
|
||||
| "MAIN_SHADE_BUTTON"
|
||||
| "MAIN_SHADE_BUTTON_DEPRESSED"
|
||||
| "MAIN_CLOSE_BUTTON"
|
||||
| "MAIN_CLOSE_BUTTON_DEPRESSED"
|
||||
| "MAIN_CLUTTER_BAR_BACKGROUND"
|
||||
| "MAIN_CLUTTER_BAR_BACKGROUND_DISABLED"
|
||||
| "MAIN_CLUTTER_BAR_BUTTON_O_SELECTED"
|
||||
| "MAIN_CLUTTER_BAR_BUTTON_A_SELECTED"
|
||||
| "MAIN_CLUTTER_BAR_BUTTON_I_SELECTED"
|
||||
| "MAIN_CLUTTER_BAR_BUTTON_D_SELECTED"
|
||||
| "MAIN_CLUTTER_BAR_BUTTON_V_SELECTED"
|
||||
| "MAIN_SHADE_BACKGROUND"
|
||||
| "MAIN_SHADE_BACKGROUND_SELECTED"
|
||||
| "MAIN_SHADE_BUTTON_SELECTED"
|
||||
| "MAIN_SHADE_BUTTON_SELECTED_DEPRESSED"
|
||||
| "MAIN_SHADE_POSITION_BACKGROUND"
|
||||
| "MAIN_SHADE_POSITION_THUMB"
|
||||
| "MAIN_SHADE_POSITION_THUMB_LEFT"
|
||||
| "MAIN_SHADE_POSITION_THUMB_RIGHT"
|
||||
| "MAIN_VOLUME_BACKGROUND"
|
||||
| "MAIN_VOLUME_THUMB"
|
||||
| "MAIN_VOLUME_THUMB_SELECTED"
|
||||
| "GEN_TOP_LEFT_SELECTED"
|
||||
| "GEN_TOP_LEFT_END_SELECTED"
|
||||
| "GEN_TOP_CENTER_FILL_SELECTED"
|
||||
| "GEN_TOP_RIGHT_END_SELECTED"
|
||||
| "GEN_TOP_LEFT_RIGHT_FILL_SELECTED"
|
||||
| "GEN_TOP_RIGHT_SELECTED"
|
||||
| "GEN_TOP_LEFT"
|
||||
| "GEN_TOP_LEFT_END"
|
||||
| "GEN_TOP_CENTER_FILL"
|
||||
| "GEN_TOP_RIGHT_END"
|
||||
| "GEN_TOP_LEFT_RIGHT_FILL"
|
||||
| "GEN_TOP_RIGHT"
|
||||
| "GEN_BOTTOM_LEFT"
|
||||
| "GEN_BOTTOM_RIGHT"
|
||||
| "GEN_BOTTOM_FILL"
|
||||
| "GEN_MIDDLE_LEFT"
|
||||
| "GEN_MIDDLE_LEFT_BOTTOM"
|
||||
| "GEN_MIDDLE_RIGHT"
|
||||
| "GEN_MIDDLE_RIGHT_BOTTOM"
|
||||
| "GEN_CLOSE_SELECTED";
|
||||
|
||||
export interface Sprite {
|
||||
name: SpriteName;
|
||||
|
|
@ -83,7 +292,8 @@ export const FONT_LOOKUP: { [letter: string]: [number, number] } = {
|
|||
"}": [1, 23],
|
||||
};
|
||||
|
||||
export const imageConstFromChar = (char: string) =>
|
||||
export const imageConstFromChar = (char: string): SpriteName =>
|
||||
// @ts-ignore
|
||||
`CHARACTER_${char.charCodeAt(0)}`;
|
||||
|
||||
const CHAR_X = 5;
|
||||
|
|
@ -128,7 +338,14 @@ const sprites: SpriteMap = {
|
|||
{ name: "MAIN_PAUSE_BUTTON_ACTIVE", x: 46, y: 18, width: 23, height: 18 },
|
||||
{ name: "MAIN_STOP_BUTTON", x: 69, y: 0, width: 23, height: 18 },
|
||||
{ name: "MAIN_STOP_BUTTON_ACTIVE", x: 69, y: 18, width: 23, height: 18 },
|
||||
{ name: "MAIN_NEXT_BUTTON", x: 92, y: 0, width: 23, height: 18 },
|
||||
{
|
||||
name: "MAIN_NEXT_BUTTON",
|
||||
x: 92,
|
||||
y: 0,
|
||||
// The actual button is 22. What gives here.
|
||||
width: 22,
|
||||
height: 18,
|
||||
},
|
||||
{ name: "MAIN_NEXT_BUTTON_ACTIVE", x: 92, y: 18, width: 22, height: 18 },
|
||||
{ name: "MAIN_EJECT_BUTTON", x: 114, y: 0, width: 22, height: 16 },
|
||||
{ name: "MAIN_EJECT_BUTTON_ACTIVE", x: 114, y: 16, width: 22, height: 16 },
|
||||
|
|
@ -824,3 +1041,11 @@ const sprites: SpriteMap = {
|
|||
};
|
||||
|
||||
export default sprites;
|
||||
|
||||
export const spritesByName: { [name: string]: Sprite } = {};
|
||||
|
||||
Object.values(sprites).forEach((s) => {
|
||||
s.forEach((sprite) => {
|
||||
spritesByName[sprite.name] = sprite;
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -403,3 +403,11 @@ export function weakMapMemoize<T extends object, R>(
|
|||
return cache.get(value);
|
||||
};
|
||||
}
|
||||
|
||||
export function imageVarName(imageName: string): string {
|
||||
return `--webamp-${imageName}`;
|
||||
}
|
||||
|
||||
export function cursorVarName(imageName: string): string {
|
||||
return `--webamp-cursor-${imageName}`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ const imagemin = require("imagemin");
|
|||
const imageminOptipng = require("imagemin-optipng");
|
||||
|
||||
const DATA_URL_REGEX = new RegExp(/url\((data:image\/png;base64,.+)\)/gi);
|
||||
const DATA_URL_PROPS_REGEX = /^(background(?:-image)?)|(content)|(cursor)/;
|
||||
// The `--` is so that we consider CSS variables.
|
||||
const DATA_URL_PROPS_REGEX = /^(background(?:-image)?)|(content)|(cursor)|(--)/;
|
||||
|
||||
async function optimizeDataUri(dataUri) {
|
||||
const buffer = dataUriToBuffer(dataUri);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue