diff --git a/css/media-library-window.css b/css/media-library-window.css new file mode 100644 index 00000000..54a9f45a --- /dev/null +++ b/css/media-library-window.css @@ -0,0 +1,81 @@ +#webamp-media-library { + font-size: 10px; + font-family: "MS Sans Serif", "Segoe UI", sans-serif; + -webkit-font-smoothing: none; + padding-right: 2px; + padding-bottom: 3px; +} + +#webamp-media-library input { + height: 15px; + border: none; + background-color: inherit; + padding: 0; +} + +#webamp-media-library th { + font-weight: normal; + text-align: left; + padding-left: 5px; + white-space: nowrap; + height: 14px; +} + +#webamp-media-library td { + white-space: nowrap; +} +#webamp-media-library ul { + list-style: none; +} + +#webamp-media-library ul { + padding: 0; + margin: 0; +} + +#webamp-media-library ul li ul { + padding-left: 10px; +} + +#webamp-media-library .webamp-media-library-table { + width: 100%; + border-spacing: unset; +} +#webamp-media-library .webamp-media-library-vertical-divider { + padding-top: 2px; + padding-bottom: 2px; + padding-left: 4px; + padding-right: 4px; + cursor: col-resize; +} + +#webamp-media-library .webamp-media-library-vertical-divider-line { + width: 1px; + height: 100%; +} + +#webamp-media-library .webamp-media-library-horizontal-divider { + padding-top: 5px; + padding-bottom: 5px; + padding-left: 0px; + padding-right: 0px; + cursor: row-resize; +} + +#webamp-media-library .webamp-media-library-horizontal-divider-line { + height: 1px; + width: 100%; +} + +#webamp-media-library button { + border: none; + color: inherit; + padding: 0; + font: inherit; + cursor: pointer; + outline: inherit; + height: 18px; + cursor: inherit; + /* TODO: I think only the center is supposed to stretch */ + background-size: 100% 18px; +} diff --git a/js/actionCreators/files.ts b/js/actionCreators/files.ts index ed996896..e319d216 100644 --- a/js/actionCreators/files.ts +++ b/js/actionCreators/files.ts @@ -110,6 +110,7 @@ export function setSkinFromArrayBuffer( } try { const skinData = await skinParser(arrayBuffer, JSZip); + // @ts-ignore TODO: We still need to type skinParser. dispatch({ type: SET_SKIN_DATA, data: { @@ -118,7 +119,8 @@ export function setSkinFromArrayBuffer( skinPlaylistStyle: skinData.playlistStyle, skinCursors: skinData.cursors, skinRegion: skinData.region, - skinGenLetterWidths: skinData.genLetterWidths + skinGenLetterWidths: skinData.genLetterWidths, + skinGenExColors: skinData.genExColors } }); } catch (e) { diff --git a/js/components/App.js b/js/components/App.js index dc8e2f05..5a7bb032 100644 --- a/js/components/App.js +++ b/js/components/App.js @@ -13,6 +13,7 @@ import WindowManager from "./WindowManager"; import MainWindow from "./MainWindow"; import PlaylistWindow from "./PlaylistWindow"; import EqualizerWindow from "./EqualizerWindow"; +import MediaLibraryWindow from "./MediaLibraryWindow"; import Skin from "./Skin"; import "../../css/webamp.css"; @@ -116,6 +117,12 @@ class App extends React.Component { analyser={media.getAnalyser()} /> ); + case WINDOWS.MEDIA_LIBRARY: + return ( + this._gotRef(id, component)} + /> + ); default: if (!w.generic) { throw new Error("Tried to render an unknown window:", id); @@ -123,7 +130,7 @@ class App extends React.Component { const Component = genWindowComponents[id]; return ( this._gotRef(id, component)} + ref={component => this._gotRef(id, component)} title={w.title} windowId={id} onFocusedKeyDown={listener => this._emitter.on(id, listener)} diff --git a/js/components/MediaLibraryWindow/AlbumsTable.tsx b/js/components/MediaLibraryWindow/AlbumsTable.tsx new file mode 100644 index 00000000..6902078e --- /dev/null +++ b/js/components/MediaLibraryWindow/AlbumsTable.tsx @@ -0,0 +1,33 @@ +import * as React from "react"; + +interface Props {} + +export default class AlbumsTable extends React.Component { + render() { + return ( +
+ + + + + + + + + + + + + + + + + +
AlbumTracks
All (1 album)1
Ben Mason1
+
+ ); + } +} diff --git a/js/components/MediaLibraryWindow/ArtistsTable.tsx b/js/components/MediaLibraryWindow/ArtistsTable.tsx new file mode 100644 index 00000000..3b1cef30 --- /dev/null +++ b/js/components/MediaLibraryWindow/ArtistsTable.tsx @@ -0,0 +1,33 @@ +import * as React from "react"; + +interface Props {} + +export default class ArtistsTable extends React.Component { + render() { + return ( +
+ + + + + + + + + + + + + + + + + + + + +
ArtistAlbumTracks
All (1 artist)11
Ben Mason11
+
+ ); + } +} diff --git a/js/components/MediaLibraryWindow/Sidebar.tsx b/js/components/MediaLibraryWindow/Sidebar.tsx new file mode 100644 index 00000000..74599e1a --- /dev/null +++ b/js/components/MediaLibraryWindow/Sidebar.tsx @@ -0,0 +1,44 @@ +import * as React from "react"; + +export default class Sidebar extends React.Component { + render() { + return ( +
+
+
    +
  • + Local Media +
      +
    • Audio
    • +
    • Video
    • +
    +
  • +
  • Playlist
  • +
  • + Devices +
      +
    • CD E:
    • +
    +
  • +
  • Internet Radio
  • +
  • Internet TV
  • +
+
+ +
+ ); + } +} diff --git a/js/components/MediaLibraryWindow/TracksTable.tsx b/js/components/MediaLibraryWindow/TracksTable.tsx new file mode 100644 index 00000000..efffb019 --- /dev/null +++ b/js/components/MediaLibraryWindow/TracksTable.tsx @@ -0,0 +1,69 @@ +import * as React from "react"; + +interface Props {} + +export default class TracksTable extends React.Component { + render() { + return ( +
+
+ Search: + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ArtistTitleAlbumLengthTrack #GenereYearFilename
Ben MasonEasyBad Pands3:251Primus2001BenMason-Easy.mp3
+
+
+ + + + + 1 item [3:25] +
+
+ ); + } +} diff --git a/js/components/MediaLibraryWindow/index.tsx b/js/components/MediaLibraryWindow/index.tsx new file mode 100644 index 00000000..ea2aab47 --- /dev/null +++ b/js/components/MediaLibraryWindow/index.tsx @@ -0,0 +1,148 @@ +import * as React from "react"; +import { connect } from "react-redux"; +import GenWindow from "../GenWindow"; +import { WINDOWS } from "../../constants"; +import "../../../css/media-library-window.css"; +import { AppState, SkinGenExColors } from "../../types"; +import * as Selectors from "../../selectors"; +import Sidebar from "./Sidebar"; +import ArtistsTable from "./ArtistsTable"; +import AlbumsTable from "./AlbumsTable"; +import TracksTable from "./TracksTable"; + +interface Props { + skinGenExColors: SkinGenExColors; +} + +interface State { + sidebarWidth: number; + topPlaylistSectionHeight: number; + artistsPanelWidth: number; +} + +class MediaLibraryWindow extends React.Component { + constructor(props: Props) { + super(props); + this.state = { + sidebarWidth: 100, + topPlaylistSectionHeight: 200, + artistsPanelWidth: 150 + }; + } + + _onMouseMove(cb: (e: MouseEvent) => void) { + const handleMouseUp = () => { + document.removeEventListener("mousemove", cb); + document.removeEventListener("mouseup", handleMouseUp); + }; + document.addEventListener("mousemove", cb); + document.addEventListener("mouseup", handleMouseUp); + } + + _handleSidebarMouseDown = (e: React.MouseEvent) => { + const { pageX: startX } = e; + const initialWidth = this.state.sidebarWidth; + this._onMouseMove((moveEvent: MouseEvent) => { + this.setState({ + sidebarWidth: initialWidth + moveEvent.pageX - startX + }); + }); + }; + + _handlePlaylistResizeMouseDown = (e: React.MouseEvent) => { + const { pageY: startY } = e; + const initialHeight = this.state.topPlaylistSectionHeight; + this._onMouseMove((moveEvent: MouseEvent) => { + this.setState({ + topPlaylistSectionHeight: initialHeight + moveEvent.pageY - startY + }); + }); + }; + + _handleArtistsResizeMouseDown = (e: React.MouseEvent) => { + const { pageX: startX } = e; + const initialWidth = this.state.artistsPanelWidth; + this._onMouseMove((moveEvent: MouseEvent) => { + this.setState({ + artistsPanelWidth: initialWidth + moveEvent.pageX - startX + }); + }); + }; + + render() { + const colors = this.props.skinGenExColors; + return ( + + {({ height, width }: { height: number; width: number }) => ( +
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ )} + + ); + } +} + +const mapStateToProps = (state: AppState) => { + return { + skinGenExColors: Selectors.getSkinGenExColors(state) + }; +}; +export default connect(mapStateToProps)(MediaLibraryWindow); diff --git a/js/components/MilkdropWindow/index.js b/js/components/MilkdropWindow/index.js index 128f7ac8..917bbb3c 100644 --- a/js/components/MilkdropWindow/index.js +++ b/js/components/MilkdropWindow/index.js @@ -125,11 +125,7 @@ class PresetsLoader extends React.Component { } return ( - + {({ height, width }) => ( { cssRules.push(`${CSS_PREFIX} ${matcher} { clip-path: url(#${id}); }`); } } + + // TODO: Find a way to make this declarative. + cssRules.push( + `#webamp-media-library { + background-color: ${props.skinGenExColors.windowBackground}; + color: ${props.skinGenExColors.windowText}; + }` + ); + cssRules.push( + `#webamp-media-library input { + caret-color: ${props.skinGenExColors.windowText}; + }` + ); + cssRules.push( + `#webamp-media-library .webamp-media-library-item { + color: ${props.skinGenExColors.itemForeground}; + background-color: ${props.skinGenExColors.itemBackground}; + border-right: 1px solid ${props.skinGenExColors.divider}; + border-bottom: 1px solid ${props.skinGenExColors.divider}; + }` + ); + cssRules.push( + `#webamp-media-library button { + color: ${props.skinGenExColors.buttonText}; + }` + ); + cssRules.push( + `#webamp-media-library .webamp-media-library-vertical-divider { + }` + ); + cssRules.push( + `#webamp-media-library .webamp-media-library-vertical-divider-line, + #webamp-media-library .webamp-media-library-horizontal-divider-line + { + background-color: ${props.skinGenExColors.divider}; + }` + ); + cssRules.push( + `#webamp-media-library .webamp-media-library-table { + color: ${props.skinGenExColors.itemForeground}; + background-color: ${props.skinGenExColors.itemBackground}; + }` + ); + + cssRules.push( + `#webamp-media-library .webamp-media-library-table thead { + color: ${props.skinGenExColors.listHeaderText}; + background-color: ${props.skinGenExColors.listHeaderBackground}; + }` + ); + + cssRules.push( + `#webamp-media-library .webamp-media-library-table thead th { + border-top: 1px solid ${props.skinGenExColors.listHeaderFrameTopAndLeft}; + border-left: 1px solid ${ + props.skinGenExColors.listHeaderFrameTopAndLeft + }; + border-bottom: 1px solid ${ + props.skinGenExColors.listHeaderFrameBottomAndRight + }; + border-right: 1px solid ${ + props.skinGenExColors.listHeaderFrameBottomAndRight + }; + }` + ); + return (
{cssRules.join("\n")} @@ -158,5 +224,6 @@ export default connect(state => ({ skinImages: state.display.skinImages, skinCursors: state.display.skinCursors, skinRegion: state.display.skinRegion, - skinGenLetterWidths: state.display.skinGenLetterWidths + skinGenLetterWidths: state.display.skinGenLetterWidths, + skinGenExColors: state.display.skinGenExColors }))(Skin); diff --git a/js/constants.ts b/js/constants.ts index fac5ce61..cbdfff7d 100644 --- a/js/constants.ts +++ b/js/constants.ts @@ -22,7 +22,8 @@ export const BANDS: Band[] = [ export const WINDOWS = { MAIN: "main", PLAYLIST: "playlist", - EQUALIZER: "equalizer" + EQUALIZER: "equalizer", + MEDIA_LIBRARY: "mediaLibrary" }; export const LOAD_STYLE: Record = { diff --git a/js/reducers/display.ts b/js/reducers/display.ts index e02058c3..11a8b5e7 100644 --- a/js/reducers/display.ts +++ b/js/reducers/display.ts @@ -4,7 +4,8 @@ import { Cursors, SkinRegion, GenLetterWidths, - PlaylistStyle + PlaylistStyle, + SkinGenExColors } from "../types"; import { createSelector } from "reselect"; @@ -43,6 +44,7 @@ export interface DisplayState { skinGenLetterWidths: GenLetterWidths | null; skinColors: string[]; // Theoretically this could be a tuple of a specific length skinPlaylistStyle: PlaylistStyle | null; + skinGenExColors: SkinGenExColors; working: boolean; closed: boolean; loading: boolean; @@ -51,6 +53,31 @@ export interface DisplayState { dummyVizData: null; // TODO: Figure out what kind of data this actually is. } +const defaultSkinGenExColors = { + itemBackground: "rgb(0,0,0)", + itemForeground: "rgb(0,255,0)", + windowBackground: "rgb(56,55,87)", + buttonText: "rgb(57,57,66)", + windowText: "rgb(255,255,255)", + divider: "rgb(117,116,139)", + playlistSelection: "rgb(0,0,198)", + listHeaderBackground: "rgb(72,72,120)", + listHeaderText: "rgb(255,255,255)", + listHeaderFrameTopAndLeft: "rgb(108,108,180)", + listHeaderFrameBottomAndRight: "rgb(36,36,60)", + listHeaderFramePressed: "rgb(18,18,30)", + listHeaderDeadArea: "rgb(36,36,60)", + scrollbarOne: "rgb(36,36,60)", + scrollbarTwo: "rgb(36,36,60)", + pressedScrollbarOne: "rgb(121,130,150)", + pressedScrollbarTwo: "rgb(78,88,110)", + scrollbarDeadArea: "rgb(36,36,60)", + listTextHighlighted: "rgb(0,198,255)", + listTextHighlightedBackground: "rgb(0,198,255)", + listTextSelected: "rgb(0,198,255)", + listTextSelectedBackground: "rgb(0,198,255)" +}; + const defaultDisplayState = { doubled: false, marqueeStep: 0, @@ -68,6 +95,7 @@ const defaultDisplayState = { dummyVizData: null, playlistScrollPosition: 0, skinGenLetterWidths: null, // TODO: Get the default value for this? + skinGenExColors: defaultSkinGenExColors, additionalVisualizers: [], zIndex: 0 }; @@ -84,7 +112,8 @@ const display = ( skinCursors, skinPlaylistStyle, skinRegion, - skinGenLetterWidths + skinGenLetterWidths, + skinGenExColors } = defaultDisplayState; return { ...state, @@ -93,7 +122,8 @@ const display = ( skinCursors, skinPlaylistStyle, skinRegion, - skinGenLetterWidths + skinGenLetterWidths, + skinGenExColors }; case TOGGLE_DOUBLESIZE_MODE: @@ -126,7 +156,8 @@ const display = ( skinPlaylistStyle: data.skinPlaylistStyle, skinCursors: data.skinCursors, skinRegion: data.skinRegion, - skinGenLetterWidths: data.skinGenLetterWidths + skinGenLetterWidths: data.skinGenLetterWidths, + skinGenExColors: data.skinGenExColors || defaultSkinGenExColors }; case TOGGLE_VISUALIZER_STYLE: return { diff --git a/js/reducers/windows.ts b/js/reducers/windows.ts index 12f9c645..df26f78d 100644 --- a/js/reducers/windows.ts +++ b/js/reducers/windows.ts @@ -96,6 +96,19 @@ const defaultWindowsState: WindowsState = { generic: false, hotkey: "Alt+E", position: { x: 0, y: 0 } + }, + [WINDOWS.MEDIA_LIBRARY]: { + title: "Winamp Library", + size: [0, 0], + open: true, + hidden: false, + shade: false, + canResize: true, + canShade: false, + canDouble: false, + generic: false, + hotkey: "Alt+E", + position: { x: 0, y: 0 } } }, browserWindowSize: { width: 0, height: 0 } diff --git a/js/selectors.ts b/js/selectors.ts index c6933503..a752fd62 100644 --- a/js/selectors.ts +++ b/js/selectors.ts @@ -430,6 +430,10 @@ export const getSkinPlaylistStyle = (state: AppState): PlaylistStyle => { ); }; +export const getSkinGenExColors = (state: AppState) => { + return state.display.skinGenExColors; +}; + export const getVisualizerStyle = (state: AppState) => fromDisplay.getVisualizerStyle(state.display); diff --git a/js/skinParser.js b/js/skinParser.js index b6e27a1e..369d8388 100644 --- a/js/skinParser.js +++ b/js/skinParser.js @@ -1,7 +1,7 @@ import SKIN_SPRITES from "./skinSprites"; import regionParser from "./regionParser"; import { LETTERS, DEFAULT_SKIN } from "./constants"; -import { parseViscolors, parseIni, getFileExtension } from "./utils"; +import { parseViscolors, parseIni, getFileExtension, objectMap } from "./utils"; const shallowMerge = objs => objs.reduce((prev, img) => Object.assign(prev, img), {}); @@ -145,16 +145,18 @@ async function genPlaylistStyle(zip) { // Winamp seems to permit colors that contain too many characters. // For compatibility with existing skins, we normalize them here. - ["normal", "current", "normalbg", "selectedbg"].forEach(colorKey => { - let color = data[colorKey]; - if (!color) { - return; + ["normal", "current", "normalbg", "selectedbg", "mbFG", "mbBG"].forEach( + colorKey => { + let color = data[colorKey]; + if (!color) { + return; + } + if (color[0] !== "#") { + color = `#${color}`; + } + data[colorKey] = color.slice(0, 7); } - if (color[0] !== "#") { - color = `#${color}`; - } - data[colorKey] = color.slice(0, 7); - }); + ); return { ...DEFAULT_SKIN.playlistStyle, ...data }; } @@ -233,24 +235,97 @@ async function genGenTextSprites(zip) { return [letterWidths, getSpriteUrisFromImg(img, sprites)]; } +async function genGenExColors(zip) { + const img = await genImgFromFilename(zip, "GENEX"); + if (img == null) { + return null; + } + + const canvas = document.createElement("canvas"); + const context = canvas.getContext("2d"); + canvas.width = img.width; + canvas.height = img.height; + context.drawImage(img, 0, 0); + + const getColorAt = x => + `rgb(${context + .getImageData(x, 0, 1, 1) + // Discard the alpha channel + .data.slice(0, 3) + .join(",")})`; + + const colors = { + // (1) x=48: item background (background to edits, listviews, etc.) + itemBackground: 48, + // (2) x=50: item foreground (text colour of edits, listviews, etc.) + itemForeground: 50, + // (3) x=52: window background (used to set the bg color for the dialog) + windowBackground: 52, + // (4) x=54: button text colour + buttonText: 54, + // (5) x=56: window text colour + windowText: 56, + // (6) x=58: colour of dividers and sunken borders + divider: 58, + // (7) x=60: selection colour for entries inside playlists (nothing else yet) + playlistSelection: 60, + // (8) x=62: listview header background colour + listHeaderBackground: 62, + // (9) x=64: listview header text colour + listHeaderText: 64, + // (10) x=66: listview header frame top and left colour + listHeaderFrameTopAndLeft: 66, + // (11) x=68: listview header frame bottom and right colour + listHeaderFrameBottomAndRight: 68, + // (12) x=70: listview header frame colour, when pressed + listHeaderFramePressed: 70, + // (13) x=72: listview header dead area colour + listHeaderDeadArea: 72, + // (14) x=74: scrollbar colour #1 + scrollbarOne: 74, + // (15) x=76: scrollbar colour #2 + scrollbarTwo: 76, + // (16) x=78: pressed scrollbar colour #1 + pressedScrollbarOne: 78, + // (17) x=80: pressed scrollbar colour #2 + pressedScrollbarTwo: 80, + // (18) x=82: scrollbar dead area colour + scrollbarDeadArea: 82, + // (19) x=84 List view text colour highlighted + listTextHighlighted: 84, + // (20) x=86 List view background colour highlighted + listTextHighlightedBackground: 86, + // (21) x=88 List view text colour selected + listTextSelected: 88, + // (22) x=90 List view background colour selected + listTextSelectedBackground: 90 + }; + + return objectMap(colors, getColorAt); +} + // A promise that, given an array buffer returns a skin style object async function skinParser(zipFileBuffer, JSZip) { const zip = await JSZip.loadAsync(zipFileBuffer); + const [ colors, playlistStyle, images, cursors, region, - genTextSprites + genTextSprites, + genExColors ] = await Promise.all([ genVizColors(zip), genPlaylistStyle(zip), genImages(zip), genCursors(zip), genRegion(zip), - genGenTextSprites(zip) + genGenTextSprites(zip), + genGenExColors(zip) ]); + console.log(genExColors); const [genLetterWidths, genTextImages] = genTextSprites || [null, {}]; @@ -260,7 +335,8 @@ async function skinParser(zipFileBuffer, JSZip) { images: { ...images, ...genTextImages }, genLetterWidths, cursors, - region + region, + genExColors }; } diff --git a/js/skinSelectors.ts b/js/skinSelectors.ts index aa12d546..3f0339b3 100644 --- a/js/skinSelectors.ts +++ b/js/skinSelectors.ts @@ -315,7 +315,8 @@ export const imageSelectors: Selectors = { GEN_MIDDLE_LEFT_BOTTOM: [".gen-window .gen-middle-left-bottom"], GEN_MIDDLE_RIGHT: [".gen-window .gen-middle-right"], GEN_MIDDLE_RIGHT_BOTTOM: [".gen-window .gen-middle-right-bottom"], - GEN_CLOSE_SELECTED: [".gen-window .gen-close:active"] + GEN_CLOSE_SELECTED: [".gen-window .gen-close:active"], + GENEX_BUTTON_BACKGROUND_UNPRESSED: ["#webamp-media-library button"] }; Object.keys(FONT_LOOKUP).forEach(character => { diff --git a/js/skinSprites.ts b/js/skinSprites.ts index d6617519..fb92f3df 100644 --- a/js/skinSprites.ts +++ b/js/skinSprites.ts @@ -722,5 +722,69 @@ export default { { name: "GEN_MIDDLE_RIGHT", x: 139, y: 42, width: 8, height: 29 }, { name: "GEN_MIDDLE_RIGHT_BOTTOM", x: 170, y: 42, width: 8, height: 24 }, { name: "GEN_CLOSE_SELECTED", x: 148, y: 42, width: 9, height: 9 } + ], + GENEX: [ + { + name: "GENEX_BUTTON_BACKGROUND_UNPRESSED", + x: 0, + y: 0, + height: 15, + width: 47 + }, + { + name: "GENEX_BUTTON_BACKGROUND_PRESSED", + x: 0, + y: 1, + height: 15, + width: 47 + }, + { name: "GENEX_SCROLL_UP_UNPRESSED", x: 0, y: 31, height: 14, width: 14 }, + { + name: "GENEX_SCROLL_DOWN_UNPRESSED", + x: 14, + y: 31, + height: 14, + width: 14 + }, + { name: "GENEX_SCROLL_UP_PRESSED", x: 28, y: 31, height: 14, width: 14 }, + { name: "GENEX_SCROLL_DOWN_PRESSED", x: 42, y: 31, height: 14, width: 14 }, + { name: "GENEX_SCROLL_LEFT_UNPRESSED", x: 0, y: 45, height: 14, width: 14 }, + { + name: "GENEX_SCROLL_RIGHT_UNPRESSED", + x: 14, + y: 45, + height: 14, + width: 14 + }, + { name: "GENEX_SCROLL_LEFT_PRESSED", x: 28, y: 45, height: 14, width: 14 }, + { name: "GENEX_SCROLL_RIGHT_PRESSED", x: 42, y: 45, height: 14, width: 14 }, + { + name: "GENEX_VERTICAL_SCROLL_HANDLE_UNPRESSED", + x: 56, + y: 31, + height: 28, + width: 14 + }, + { + name: "GENEX_VERTICAL_SCROLL_HANDLE_PRESSED", + x: 70, + y: 31, + height: 28, + width: 14 + }, + { + name: "GENEX_HORIZONTAL_SCROLL_HANDLE_UNPRESSED", + x: 84, + y: 31, + height: 14, + width: 28 + }, + { + name: "GENEX_HORIZONTAL_SCROLL_HANDLE_PRESSED", + x: 84, + y: 45, + height: 14, + width: 28 + } ] }; diff --git a/js/types.ts b/js/types.ts index 9bf0a1d7..b1316517 100644 --- a/js/types.ts +++ b/js/types.ts @@ -50,6 +50,31 @@ export type SkinImages = { [sprite: string]: string }; // TODO: type these keys export type SkinRegion = { [windowName: string]: string[] }; +export interface SkinGenExColors { + itemBackground: string; + itemForeground: string; + windowBackground: string; + buttonText: string; + windowText: string; + divider: string; + playlistSelection: string; + listHeaderBackground: string; + listHeaderText: string; + listHeaderFrameTopAndLeft: string; + listHeaderFrameBottomAndRight: string; + listHeaderFramePressed: string; + listHeaderDeadArea: string; + scrollbarOne: string; + scrollbarTwo: string; + pressedScrollbarOne: string; + pressedScrollbarTwo: string; + scrollbarDeadArea: string; + listTextHighlighted: string; + listTextHighlightedBackground: string; + listTextSelected: string; + listTextSelectedBackground: string; +} + export type WindowId = string; // TODO: Fill these out once we actually use them. @@ -60,6 +85,7 @@ type SkinData = { skinCursors: Cursors; skinRegion: SkinRegion; skinGenLetterWidths: GenLetterWidths; + skinGenExColors: SkinGenExColors | null; }; export type Action =