First stab at media library

This commit is contained in:
Jordan Eldredge 2018-10-09 23:24:56 -07:00
parent 0c2b1a1c2f
commit 01f3b1a0ea
18 changed files with 723 additions and 27 deletions

View file

@ -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;
}

View file

@ -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) {

View file

@ -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 (
<MediaLibraryWindow
ref={component => 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 (
<Component
chromeRef={component => this._gotRef(id, component)}
ref={component => this._gotRef(id, component)}
title={w.title}
windowId={id}
onFocusedKeyDown={listener => this._emitter.on(id, listener)}

View file

@ -0,0 +1,33 @@
import * as React from "react";
interface Props {}
export default class AlbumsTable extends React.Component<Props> {
render() {
return (
<div className="webamp-media-library-item" style={{ flexGrow: 1 }}>
<table
className="webamp-media-library-table"
style={{ overflow: "scroll" }}
>
<thead>
<tr>
<th>Album</th>
<th>Tracks</th>
</tr>
</thead>
<tbody>
<tr>
<td>All (1 album)</td>
<td>1</td>
</tr>
<tr>
<td>Ben Mason</td>
<td>1</td>
</tr>
</tbody>
</table>
</div>
);
}
}

View file

@ -0,0 +1,33 @@
import * as React from "react";
interface Props {}
export default class ArtistsTable extends React.Component<Props> {
render() {
return (
<div className="webamp-media-library-item" style={{ height: "100%" }}>
<table className="webamp-media-library-table">
<thead>
<tr>
<th>Artist</th>
<th>Album</th>
<th>Tracks</th>
</tr>
</thead>
<tbody>
<tr>
<td>All (1 artist)</td>
<td>1</td>
<td>1</td>
</tr>
<tr>
<td>Ben Mason</td>
<td>1</td>
<td>1</td>
</tr>
</tbody>
</table>
</div>
);
}
}

View file

@ -0,0 +1,44 @@
import * as React from "react";
export default class Sidebar extends React.Component {
render() {
return (
<div
style={{
display: "flex",
flexDirection: "column",
height: "100%"
}}
>
<div className="webamp-media-library-item" style={{ flexGrow: 1 }}>
<ul>
<li>
Local Media
<ul>
<li>Audio</li>
<li>Video</li>
</ul>
</li>
<li>Playlist</li>
<li>
Devices
<ul>
<li>CD E:</li>
</ul>
</li>
<li>Internet Radio</li>
<li>Internet TV</li>
</ul>
</div>
<button
style={{
width: "100%",
marginTop: 1
}}
>
Library
</button>
</div>
);
}
}

View file

@ -0,0 +1,69 @@
import * as React from "react";
interface Props {}
export default class TracksTable extends React.Component<Props> {
render() {
return (
<div
style={{
display: "flex",
flexDirection: "column",
flexGrow: 2
}}
>
<div
style={{ paddingBottom: 3, display: "flex", flexDirection: "row" }}
>
<span>Search:</span>
<input
style={{ marginLeft: 12, flexGrow: 1 }}
type="text"
className="webamp-media-library-item"
/>
</div>
<div
style={{ flexGrow: 1, overflowY: "scroll" }}
className="webamp-media-library-item"
>
<table
className="webamp-media-library-table"
style={{ overflow: "scroll" }}
>
<thead>
<tr>
<th>Artist</th>
<th>Title</th>
<th>Album</th>
<th>Length</th>
<th>Track #</th>
<th>Genere</th>
<th>Year</th>
<th>Filename</th>
</tr>
</thead>
<tbody>
<tr>
<td>Ben Mason</td>
<td>Easy</td>
<td>Bad Pands</td>
<td>3:25</td>
<td>1</td>
<td>Primus</td>
<td>2001</td>
<td>BenMason-Easy.mp3</td>
</tr>
</tbody>
</table>
</div>
<div style={{ marginTop: 2 }}>
<button>Play</button>
<button>Enqueue</button>
<button>Play all</button>
<button>Enqueue all</button>
<span>1 item [3:25]</span>
</div>
</div>
);
}
}

View file

@ -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<Props, State> {
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<HTMLDivElement>) => {
const { pageX: startX } = e;
const initialWidth = this.state.sidebarWidth;
this._onMouseMove((moveEvent: MouseEvent) => {
this.setState({
sidebarWidth: initialWidth + moveEvent.pageX - startX
});
});
};
_handlePlaylistResizeMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
const { pageY: startY } = e;
const initialHeight = this.state.topPlaylistSectionHeight;
this._onMouseMove((moveEvent: MouseEvent) => {
this.setState({
topPlaylistSectionHeight: initialHeight + moveEvent.pageY - startY
});
});
};
_handleArtistsResizeMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
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 (
<GenWindow title={"Winamp Library"} windowId={WINDOWS.MEDIA_LIBRARY}>
{({ height, width }: { height: number; width: number }) => (
<div
id="webamp-media-library"
style={{
position: "absolute",
top: 0,
bottom: 0,
left: 0,
right: 0,
display: "flex",
flexDirection: "row",
overflow: "hidden"
}}
>
<div style={{ width: this.state.sidebarWidth }}>
<Sidebar />
</div>
<div
className="webamp-media-library-vertical-divider"
onMouseDown={this._handleSidebarMouseDown}
>
<div className="webamp-media-library-vertical-divider-line" />
</div>
<div
style={{
display: "flex",
flexDirection: "column",
flexShrink: 1,
flexGrow: 1,
// https://stackoverflow.com/a/35609992/1263117
overflow: "hidden"
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
flexShrink: 0,
height: this.state.topPlaylistSectionHeight
}}
>
<div style={{ width: this.state.artistsPanelWidth }}>
<ArtistsTable />
</div>
<div
className="webamp-media-library-vertical-divider"
onMouseDown={this._handleArtistsResizeMouseDown}
>
<div className="webamp-media-library-vertical-divider-line" />
</div>
<AlbumsTable />
</div>
<div
className="webamp-media-library-horizontal-divider"
onMouseDown={this._handlePlaylistResizeMouseDown}
>
<div className="webamp-media-library-horizontal-divider-line" />
</div>
<TracksTable />
</div>
</div>
)}
</GenWindow>
);
}
}
const mapStateToProps = (state: AppState) => {
return {
skinGenExColors: Selectors.getSkinGenExColors(state)
};
};
export default connect(mapStateToProps)(MediaLibraryWindow);

View file

@ -125,11 +125,7 @@ class PresetsLoader extends React.Component {
}
return (
<GenWindow
ref={this.props.chromeRef}
title={this.props.title}
windowId={this.props.windowId}
>
<GenWindow title={this.props.title} windowId={this.props.windowId}>
{({ height, width }) => (
<ContextMenuWrapper
onDoubleClick={this._handleRequestFullsceen}

View file

@ -146,6 +146,72 @@ const Skin = props => {
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 (
<div>
<Css>{cssRules.join("\n")}</Css>
@ -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);

View file

@ -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<LoadStyle, LoadStyle> = {

View file

@ -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 {

View file

@ -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 }

View file

@ -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);

View file

@ -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
};
}

View file

@ -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 => {

View file

@ -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
}
]
};

View file

@ -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 =